Introduction

Scenario

My client is an industry-leading energy infrastructure provider based in Europe. Following onto the energy crisis that Europe is facing (Popkostova, 2022), they are in the process of securing more funding from investors, and to invest in energy projects in Europe to meet the ever-growing electricity demand.

Their renewable energy project team is eager to discover insights from the EV market. They would like to know if EV market is impacting the electricity demand in Europe and by what magnitude, so that data-driven decisions could be made based on conclusions drawn from this data analysis project. Once we produce actionable recommendations to them, they could then conduct feasibility study based on our conclusion and advise their board and stakeholders.

Ask

Define metrics from business objectives

Following an interview with my client, we have established these objectives as key tasks:

  1. Identify historical trends of the European EV market.
  2. Identify short-term investment opportunities based on historical and current demand.
  3. Identify long-term investment opportunities based on projected future demands.

To accomplish these objectives, we would be required to procure relevant and up to date data sources that are related to the EV market in Europe, process and analyze them in order to draw conclusions and be able to make actionable data-driven recommendations. We need to quantify the demand, trends and to visualize these in turn to help our stakeholders to understand the insights discovered from our analysis.

Identify key stakeholders

The stakeholders of this project would include:

  • Project manager of the renewable energy project team
  • Director of strategic investment in Energy and The Head of EU projects
  • Board of directors

Prepare

Source and credibility of the data

The datasets used are sourced from IEA (International Energy Agency), a Paris-based autonomous intergovernmental organisation that serves as a policy adviser on energy issues for 30 member countries. The datasets contain records of EV sales, publicly available EV chargers and EV electricity demand sorted by countries and regions between 2010-2020 (Global EV Data Explorer – Analysis - IEA, 2022). Considering that the EV market is still in its infancy as the first mass produced EV is only released in 1997 (The History of the Electric Car, 2022), we can assume that the dataset used would be relevant and up to date to reflect recent market trends.

Licensing

This project will not include the original dataset as downloadable file as to adhere to IEA’s terms and conditions, which states anything greater than 5 (five) numerical data points (but still an Insubstantial Amount) from the Material must not be made available in a separate downloadable format and must be presented either in graphical format or aggregated (in such a manner that the reader cannot reverse engineer or extract the original underlying numerical data).

This also means that the analysis process will only include the summary or head of a dataset, where appropriate.

How is the data organized?

The datasets used are three seperate csv files and their corresponding naming convention used to name dataframes:

  • IEA-EV-dataEV salesCarsHistorical.csv (EVsales)

This dataset contains the sales data of Electric Vehicles from 2010 to 2020 in different countries and regions. Key information includes: Types of EV.

  • IEA-EV-dataEV chargersHistorical.csv (EVchargers)

This dataset contains the publicly available chargers of Electric Vehicles from 2010 to 2020 in different countries and regions. Key information includes: Types of chargers.

  • IEA-EV-dataElectricity demandCarsHistorical_MoMo.csv (EVdemand)

This dataset contains the electricity demand of Electric Vehicles from 2015 to 2020. Key information includes: Electricity demand based on the type of EV.

We will load them into dataframes to examine how they are organized.

library(dplyr, warn.conflicts = FALSE)
options(dplyr.summarise.inform = FALSE)
library(tidyverse, warn.conflicts = FALSE)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages -------------------------------------------------------------------------------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v purrr   0.3.4
v tibble  3.1.6     v stringr 1.4.0
v tidyr   1.1.4     v forcats 0.5.1
v readr   2.1.1     
-- Conflicts ----------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
EVsales <- read.csv(url("https://api.iea.org/evs/?parameter=EV%20sales&mode=Cars&category=Historical&csv=true"))
EVchargers <- read.csv(url("https://api.iea.org/evs/?parameter=EV%20chargers&category=Historical&csv=true"))
EVdemand <- read.csv(url("https://api.iea.org/evs/?parameter=Electricity%20demand&mode=Cars&category=Historical_MoMo&csv=true"))

head(EVsales)
head(EVchargers)
head(EVdemand)

Potential problems with the data

Upon the examination of data above, we recognize the potential problems below:

  • some columns in each dataset is redundant (i.e. not useful to analyze them)
  • Datasets could contain different time range (EV electricity demand dataset seems to start from 2015, instead of 2010)
  • some of the fields could be missing (i.e. contains no data)
  • The default datatype assigned by R could be inappropriate for purpose of analysis

We will pay attention and attempt to overcome these problems in the next step - Process

Process

Data cleaning and pre-processing

First, let’s explore which regions are included in our EV sales dataset. To do this, we use the unique() function on “region” and “year” variable to access only the unique values.

unique(EVsales[c("region")])
unique(EVsales[c("year")])

Here we have 11 years of data to look at, from 2010 to 2020.

Next, we take a look at some seemingly irrelevant columns. We could then drop these columns if they are indeed redundant and proves to be not useful for the purpose of our data analysis.

unique(EVsales[c("category")])
unique(EVsales[c("parameter")])
unique(EVsales[c("mode")])
unique(EVsales[c("unit")])

These 4 columns only contains singular categorical data which would not be useful for our analysis, hence we will drop these columns from our dataframes.

EVsales_cleaned <- subset(EVsales, select = -c(category, parameter, mode, unit))
glimpse(EVsales_cleaned)
Rows: 704
Columns: 4
$ region     <chr> "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Austra~
$ powertrain <chr> "BEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV~
$ year       <int> 2011, 2012, 2012, 2013, 2013, 2014, 2014, 2015, 2015, 2016, 2016, 2017, 2017, 2018, 2018, 2019, 2019, 2020, 2020, 2011, 2011~
$ value      <dbl> 49.0, 173.0, 80.0, 191.0, 102.0, 371.0, 951.0, 759.0, 1012.0, 668.0, 701.0, 1208.0, 1076.0, 1803.0, 1802.0, 6283.0, 2877.0, ~

Now our EVsales dataframe only contains 4 useful attributes: region, powertrain, year and value. We will continue with the next steps of data processing.

Despite that the data is sourced from a credible organization and most likely to be cleaned already, we will still search for any potential null or erroneous value in order to make sure we are using clean and correct data.

EVsales_cleaned <- drop_na(EVsales_cleaned)
glimpse(EVsales_cleaned)
Rows: 704
Columns: 4
$ region     <chr> "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Australia", "Austra~
$ powertrain <chr> "BEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV", "BEV", "PHEV~
$ year       <int> 2011, 2012, 2012, 2013, 2013, 2014, 2014, 2015, 2015, 2016, 2016, 2017, 2017, 2018, 2018, 2019, 2019, 2020, 2020, 2011, 2011~
$ value      <dbl> 49.0, 173.0, 80.0, 191.0, 102.0, 371.0, 951.0, 759.0, 1012.0, 668.0, 701.0, 1208.0, 1076.0, 1803.0, 1802.0, 6283.0, 2877.0, ~

Here we notice that the value datatype is double. Since there can’t be ‘0.297’ or half of a car, we will convert the datatype on “value” column to integer to enforce data integrity.

EVsales_cleaned <- transform(EVsales_cleaned, value = as.integer(value))

Since we only want to explore the EV market trends in Europe, we could exclude rest of regions outside the European continent. Here we create a vector of European countries from the “region” data we saw above, and then dropping rows which regions does not match.

list_of_EU_countries <- c("Belgium", "Denmark", "Finland", "France", "Germany", "Greece", "Iceland", "Italy", "Netherlands", "Norway", "Other Europe", "Poland", "Portugal", "Spain", "Sweden", "Switzerland", "United Kingdom")
EVsales_cleaned <- subset(EVsales_cleaned, region %in% list_of_EU_countries)

Here is a glimpse of our cleaned EVsales dataframe:

head(EVsales_cleaned)

Next, we will apply the same data cleaning techniques to the other two datasets we have.

unique(EVchargers[c("category")])
unique(EVchargers[c("parameter")])
unique(EVchargers[c("mode")])
unique(EVchargers[c("unit")])
unique(EVchargers[c("powertrain")])
unique(EVchargers[c("year")])

We’re keeping powertrain and dropping the other 4 since they contain no data or useless data.

EVchargers_cleaned <- subset(EVchargers, select = -c(category, parameter, mode, unit))
EVchargers_cleaned <- drop_na(EVchargers_cleaned)
EVchargers_cleaned <- transform(EVchargers_cleaned, value = as.integer(value))
EVchargers_cleaned <- subset(EVchargers_cleaned, region %in% list_of_EU_countries)
head(arrange(EVchargers_cleaned, year))

Printing unique values in EVdemand dataframe.

unique(EVdemand[c("region")])
unique(EVdemand[c("category")])
unique(EVdemand[c("parameter")])
unique(EVdemand[c("mode")])
unique(EVdemand[c("unit")])
unique(EVdemand[c("powertrain")])
unique(EVdemand[c("year")])

Here we can drop category, parameter, mode and unit attributes again, then dropping rows containing null values before only including Europe region data.

EVdemand_cleaned <- subset(EVdemand, select = -c(category, parameter, mode, unit))
EVdemand_cleaned <- drop_na(EVdemand_cleaned)
EVdemand_cleaned <- subset(EVdemand_cleaned, region =="Europe")
head(arrange(EVdemand_cleaned, year))

Procedures of data cleaning and processing

  • All dataset were examined by looking at unique values of each variable.
  • Columns that are not useful to our analysis objective were dropped.
  • Observations with any empty values were dropped (if there were any).
  • Rows where region is not part of Europe were dropped.
  • Data types were amended where appropriate (i.e. EV sales should be a whole number and electricity demand could be a float.)

Here is a summary of each dataframe before the procedures and after:

EVsales: 704 rows, 8 variables.

EVsales_cleaned: 422 rows, 4 variables.

EVchargers: 464 rows, 8 variables.

EVchargers_cleaned: 284 rows, 4 variables.

EVdemand: 56 rows, 8 variables.

EVdemand_cleaned: 12 rows, 4 variables.

Analyze

import our libraries ggplot2, viridis and plotly for visualization.

library(ggplot2, warn.conflicts = FALSE)
library(viridis, warn.conflicts = FALSE)
Loading required package: viridisLite
library(plotly, warn.conflicts = FALSE)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

First, we produce a simple line plot of the total EV sales of European countries between 2010 and 2020.

options(scipen=1000)
plt_TotalEVsales <- data.frame(EVsales_cleaned %>% group_by(year) %>% summarize("TotalEVsales" = sum(value)))
plt_TotalEVsales <- plt_TotalEVsales %>% ggplot(aes(x=year, y=TotalEVsales)) +
  geom_line()+
  labs(x = "Year", 
       y = "Numbers of EV sold",
       title = "EV sales of European Countries Combined (2010-2020)")+
  theme_classic()+
  scale_x_continuous(breaks = 2010:2020)
ggplotly(plt_TotalEVsales, width=800)

Insight 1

From the line plot, we can see that the Total EV sales in Europe has been consistently growing over the time period between 2010 and 2020, with accelerated growth especially during 2016 to 2020. Next, we look deeper into the data by plotting annual sales data sorted by region(countries).

plt_EVsalesByRegion <- data.frame(EVsales_cleaned %>% group_by(region,year) %>% summarize(sum(value)))

plt_EVsalesByRegion <- plt_EVsalesByRegion %>% ggplot(aes(x=year, y=sum.value., color =region)) +
  geom_line()+
  labs(x = "Year", 
       y = "Numbers of EV sold",
       title = "EV sales by European Countries (2010-2020)")+
  theme_classic()+
  scale_x_continuous(breaks = 2010:2020)
ggplotly(plt_EVsalesByRegion, width=800)

Insight 2

We can see that Germany, France and the United Kingdom are the leading top 3 countries in the sales of EV in Europe. Also, this group of 3 appears to have the highest rate of growths too, we will investigate this further with a different dataset later on.

Next, we look at the sales different types of EVs in terms of powertrain.

plt_EVsalesByPowertrain <- data.frame(EVsales_cleaned %>% group_by(powertrain) %>% summarize(sum(value)))
plt_EVsalesByPowertrain <- plt_EVsalesByPowertrain %>% ggplot(aes(x="", y=sum.value., fill=powertrain)) +
    geom_bar(width = 1, stat = "identity", color="white")+
  labs(title = "Total EV sales by powertrain types, \nEurope Combined (2010-2020)")+
  scale_fill_manual(values = c("slateblue1", "gold", "sienna3"))+
  theme_void()
plt_EVsalesByPowertrain <- plt_EVsalesByPowertrain + geom_text(aes(label = sprintf("%0.2f%%", round((sum.value. / sum(sum.value.)*100), digits = 2))),
            position = position_stack(vjust = 0.5)) +
  coord_polar(theta = "y")
plt_EVsalesByPowertrain

Insight 3

Out of 3 types of EVs included in the EV sales dataset, Battery Electric Vehicle(BEV) is the most popular choice accounted for 54.68% of the total sales. Plug-in Hybrid Electric Vehicles(PHEV) follows slightly behind with 45.25%. Finally, Fuel Cell Electric vehicle(FCEV) seems to be a niche choice for consumers as they only accounts for 0.07% of the total sales between 2010-2020.

Next, we will look at public charger types in all of Europe.

plt_EVchargersPct <- data.frame(EVchargers_cleaned %>% group_by(powertrain) %>% summarize(sum(value)))
plt_EVchargersPct <- plt_EVchargersPct %>% ggplot(aes(x="", y=sum.value., fill=powertrain)) +
    geom_bar(width = 1, stat = "identity", color="white")+
  labs(title = "Charger types in percentage, Europe Combined (2020)")+
  scale_fill_manual(values = c("slateblue1", "gold", "sienna3"))+
  theme_void()
plt_EVchargersPct <- plt_EVchargersPct + geom_text(aes(label = sprintf("%0.2f%%", round((sum.value. / sum(sum.value.)*100), digits = 2))),
            position = position_stack(vjust = 0.5)) +
  coord_polar(theta = "y")
plt_EVchargersPct

Insight 4

As of 2020, roughly only 1 out of 10 publicly available chargers in Europe is a fast charger.

Now, we plot the number of chargers vs. countries in 2020.

plt_EVchargersByRegion <- ggplot(data = EVchargers_cleaned %>% filter(year=="2020"), aes(y=reorder(region, value), x=value, fill =powertrain)) +
    geom_col()+
  labs(x = "Numbers of Chargers", 
       y = "Countries",
       title = "Public available EV chargers by European Countries (2020)")+
  scale_fill_manual(values = c("slateblue1", "gold"))
ggplotly(plt_EVchargersByRegion, width=800)

Insight 5

Countries such as Netherlands, Italy and Belgium has a small fast chargers to slow chargers ratio.

Next, we explore how many public chargers are available per EV in different European countries.

df1 <- EVchargers_cleaned %>% filter(year=="2020") %>% group_by(region) %>% summarize("EVchargers" = sum(value))
df2 <- EVsales_cleaned %>% group_by(region) %>% summarize("EVsalescombined" = sum(value))
df3 <- merge(df1, df2, by="region")
df3$EVsPerCharger <- df3$EVsalescombined / df3$EVchargers
head(df3)

visualzing the data, dashed line is the mean value of how many EVs are there per publicly available charger.

pltEVvsChargers <- ggplot(df3, aes(y=reorder(region, EVsalescombined), x=EVsPerCharger, fill="EVsPerCharger")) +
    geom_col()+
  labs(x = "No. of EVs", 
       y = "Countries ranked in order of No. of EV Sales",
       title = "Numbers of EVs Per Charger (Publicly Available Slow + Fast)")+
  scale_fill_manual(values = c("slateblue1"))+
  geom_vline(xintercept = mean(df3$EVsPerCharger), color="black", linetype = "dashed")
ggplotly(pltEVvsChargers, width=800) %>% layout(showlegend = FALSE)

Insight 6

Countries such as Germany, Norway and Sweden has higher than average of EV per charger ratio (One charger has serves more EVs).

Simple linear regression on EV sales and EV electricity demand

To forecast future EV electricity demand, we must first identify the relationship between our predictor variables(BEV and PHEV sales) and response variable (EV electricity demand).

First, we perform a simple linear regression on EV sales as our predictor variable and EV electricity demand as our response variable. We will manipulate the EV sales data and turn it into cumulative sum, since the nature of EV electricity demand data is cumulative (Each datapoint of EV demand is cumulative to all previous EV sold, as we assume EVs sold in the past also contribute towards the demand)

unique(EVdemand_cleaned$year)
[1] 2015 2016 2017 2018 2019 2020
unique(EVsales_cleaned$year)
 [1] 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2010

We can see that the EV electricity demand dataset only have Europe region data available between 2015 and 2020, we will use this time period for our regression analyses later on.

library(forecast, warn.conflicts = FALSE)
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
library(MASS)

Attaching package: ‘MASS’

The following object is masked from ‘package:plotly’:

    select

The following object is masked from ‘package:dplyr’:

    select
df <- inner_join(EVdemand_cleaned %>% group_by(year) %>% summarize("EVdemand" = sum(value)), EVsales_cleaned %>% group_by(year) %>% summarize("EVsales" = sum(value)))
Joining, by = "year"
df$cumsumEVsales <- (cumsum(df$EVsales))
df

Here we first visualize the data via a scatter plot:

plt_lm1 <- ggplot(df,aes(x=cumsumEVsales,y=EVdemand))+
         geom_point()+
         theme_classic()+
  geom_smooth(formula = y ~ x, method=lm,se=FALSE,fullrange=TRUE)
ggplotly(plt_lm1)

The plot shows that the linear model fits almost perfectly, we will examine this with a correlation test.

cor.test(df$cumsumEVsales, df$EVdemand)

    Pearson's product-moment correlation

data:  df$cumsumEVsales and df$EVdemand
t = 62.023, df = 4, p-value = 0.0000004047
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.9950170 0.9999459
sample estimates:
      cor 
0.9994805 

Insight 7

The p value for this model is very low (<0.05) and hence we could assume high statistical significance, rejecting the null hypothesis. The correlation coefficient of 0.9994805 also suggest a high positive correlation between the cumulative sales of EV and the EV electricity demand.

Since our dataset are comprised of categorical data such as region and powertrain, we need to use multiple linear regression in order to create a more accurate model and in turn produce more confident forecast results.Hence we will perform multiple linear regression on BEV vs. BEV electricity demands and PHEV vs. PHEV electricity demand.

Multiple linear regression

First, we construct a dataframe from the cumulative sum of annual BEV and PHEV sales in European countries and their corresponding annual electricity demand. we will also calculate the electricity demand per vehicle for EV and PHEV and convert the unit into Kilowatt Hour (KWh) from the original GWh from the dataset. FCEV sales data is excluded from the multiple regression analysis as EVdemand dataset does not contain relevant data.

df4 <- EVsales_cleaned %>% group_by(year, powertrain) %>% summarise("value" = sum(value)) %>% pivot_wider(names_from = "powertrain", values_from = "value") %>% rename(sales_BEV = BEV, sales_FCEV = FCEV, sales_PHEV = PHEV)
df4$cumsumBEVsales <- (cumsum(df4$sales_BEV))
df4$cumsumPHEVsales <- (cumsum(df4$sales_PHEV))
df4 <- subset(df4, select = -c(sales_BEV, sales_PHEV, sales_FCEV))
df7 <- EVdemand_cleaned %>% group_by(year, powertrain) %>% summarise("value" = sum(value)) %>% pivot_wider(names_from = "powertrain", values_from = "value") %>% rename(EVdemand_BEV = BEV, EVdemand_PHEV = PHEV)
df7$EVdemand_Total <- df7$EVdemand_BEV + df7$EVdemand_PHEV
df6 <- merge(df4, df7, by="year")
df6$EDperBEV <- (df6$EVdemand_BEV / df6$cumsumBEVsales) * 1e6
df6$EDperPHEV <- (df6$EVdemand_PHEV / df6$cumsumPHEVsales) * 1e6
df6

Although we know that EV sales and combined Electricity demand is positively correlated, we still want to find out how strong the correlation is for each type of the EV based on powertrain. We can achieve that by produce scatter plots for both BEV and PHEV to visualize their linear relationship:

spBEV <- plot_ly(x = df6$cumsumBEVsales, y = df6$EVdemand_BEV, type = 'scatter', mode = 'markers', name = 'BEV',
                marker = list(line = list(width = 3)), width=800) %>%
  layout(plot_bgcolor='#e5ecf6', 
         xaxis = list( 
           zerolinecolor = '#ffff', 
           zerolinewidth = 2, 
           gridcolor = 'ffff'), 
         yaxis = list( 
           zerolinecolor = '#ffff', 
           zerolinewidth = 2, 
           gridcolor = 'ffff'))

spPHEV <- plot_ly(x = df6$cumsumPHEVsales, y = df6$EVdemand_PHEV, type = 'scatter', mode = 'markers', name = 'PHEV',
                marker = list(line = list(width = 3)), width=800) %>%
  layout(plot_bgcolor='#e5ecf6', 
         xaxis = list( 
           zerolinecolor = '#ffff', 
           zerolinewidth = 2, 
           gridcolor = 'ffff'), 
         yaxis = list( 
           zerolinecolor = '#ffff', 
           zerolinewidth = 2, 
           gridcolor = 'ffff'))

sp2 <- subplot(spBEV, spPHEV, margin = 0.07) %>% 
  layout(title = 'EV sales vs EV electricity Demand (2015-2020, Europe)')
sp2
cor(df6$cumsumBEVsales, df6$EVdemand_BEV)
[1] 0.9993526
cor(df6$cumsumPHEVsales, df6$EVdemand_PHEV)
[1] 0.9990655

Correlation test shows that both relationship are almost perfectly positively correlated.

Since we also calculated the electricity demand per vehicle for EV and PHEV, we can then further analyze and visualize by producing a boxplot based on these data.

bpEDperBEV <- plot_ly(y = df6$EDperBEV, type = "box", name = 'BEV Electricity Demand',  width=800)
bpEDperPHEV <- plot_ly(y = df6$EDperPHEV, type = "box", name = 'PHEV Electricity Demand',  width=800)
bp1 <- subplot(bpEDperBEV, bpEDperPHEV, margin = 0.07) %>% 
  layout(title = 'Average Annual Electricity Demand per EV (KWh), 2015-2020')
bp1

Insight 8

The Average annual electricity demand per BEV has a higher range than the demand of PHEV. The mean of BEV electricity demand in terms of kilowatt hour is also higher than PHEV’s, which is to be expected since PHEV usually have a smaller capacity battery than most BEV. There is also an outlier in PHEV electricity demand that is significantly below its usual value. The interquartile range of BEV electricity demand is larger than its PHEV counterpart.

Next, we construct the multiple regression model.

model <- lm(EVdemand_Total ~ cumsumBEVsales + cumsumPHEVsales, data = df6)
summary(model)

Call:
lm(formula = EVdemand_Total ~ cumsumBEVsales + cumsumPHEVsales, 
    data = df6)

Residuals:
      1       2       3       4       5       6 
 -2.416  90.224 -12.815 -33.204 -98.362  56.573 

Coefficients:
                    Estimate   Std. Error t value Pr(>|t|)  
(Intercept)     -180.5412603   77.0435963  -2.343   0.1009  
cumsumBEVsales     0.0015376    0.0007722   1.991   0.1405  
cumsumPHEVsales    0.0032042    0.0009600   3.338   0.0445 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 86.19 on 3 degrees of freedom
Multiple R-squared:  0.9992,    Adjusted R-squared:  0.9987 
F-statistic:  1895 on 2 and 3 DF,  p-value: 0.00002224

Insight 9

The p value here is highly significant which means that at least one of the predictor variables is significantly related to total EV electricity demand. The adjusted R squared value of 0.9987 means that 99.87% of the variance could be explained by cumulative EV sales. The Residual standard error tells us that the model predicts the electricity demand of EVs with an average error of about 86.19 GWh. The estimated regression line equation is roughly: EVdemand_Total = -180.54 + 0.00154 * cumsumBEVsales + 0.00320 * cumsumPHEVsales

Time series analysis using ARIMA modelling

As we have a model ready to forecast EV electricity demand, we also need forecast data for EV sales to fit to the model as new data. First, we manipulate the data into a new dataframe. We want our EV sales data to be pivoted wide in order to run this through a loop.

df8 <- EVsales_cleaned %>% filter(powertrain !="FCEV") %>% group_by(year, powertrain, region) %>% summarise("value" = sum(value)) %>% pivot_wider(names_from = "region", values_from = "value", values_fill = 0)
df8 <- df8 %>% pivot_longer('Denmark':'Greece', names_to = "region", values_to = "value")
df8$powertrainregion <- paste(df8$powertrain, df8$region)
df8 <- df8 %>% ungroup()
df8 <- subset(df8, select = -c(region, powertrain))
df8 <- df8 %>% pivot_wider(names_from = "powertrainregion", values_from = "value")
head(df8)

The data is still in annualized sales data, we need cumulative sum of the sales data in order to perform auto.arima function. Here we loop through the dataframe to calculate cumulative sum.

df9 <- as.data.frame(df8$year)
names(df9)[1] <- 'year'
for(i in 2:ncol(df8)){
  df9[i] <- cumsum(df8[ , c(i)])
}
df9 <- df9 %>% rename_all(function(x) gsub(" ", "_", x))
head(df9)

Now we have our data ready, it is time to loop through it once more with arima and forecast functions.

EVsalesProjection <- data.frame(matrix(0, nrow = 10, ncol = 35))
colnames(EVsalesProjection) <- colnames(df9)
EVsalesProjection$year <- c(2021:2030)
for(i in 2:ncol(df9)){
  EVsalesProjection[i] <- forecast(auto.arima(df9[i]))$mean
}
EVsalesProjection

Now that we have our projected sales data, we will run them through yet another loop to fit new data to our regression model.

fit <- lm(EVdemand_Total ~ cumsumBEVsales + cumsumPHEVsales, data = df6)
fcastdf <- data.frame(matrix(0, nrow = 10, ncol = 18))
colnames(fcastdf) <- colnames(EVsalesProjection)[1:18]
names(fcastdf) <- substring(names(fcastdf), 5)
fcastdf[1] <- 2021:2030
fcastdf <- rename(fcastdf, year = "")

for(i in 2:18){
tempdf <- data.frame(matrix(0, nrow = 10, ncol = 2))
tempdf <- subset(EVsalesProjection, select = c(i,i+17))
tempdf <- tempdf %>% rename(cumsumBEVsales = 1, cumsumPHEVsales = 2)
fcastdf[i] <- (forecast(fit, newdata=tempdf))$mean
}
head(fcastdf)

Here is a simple visualization in R, we will also export and visualize the forecast dataset in Tableau.

plt_fcastEVdemand <- data.frame(fcastdf %>% group_by(year) %>% pivot_longer(2:18, names_to = "region", values_to = "value") %>% group_by(year,region) %>% summarize(electricity_demand = sum(value)))
plt_fcastEVdemand <- plt_fcastEVdemand %>% ggplot(aes(x=year, y=electricity_demand, color=region)) +
  geom_line()+
  labs(x = "Year", 
       y = "Numbers of EV sold",
       title = "EV electricity demand forecast in GWh (2021-2030)")+
  theme_classic()+
  scale_x_continuous(breaks = 2021:2030)
ggplotly(plt_fcastEVdemand, width=800)

Insight 10

Based on our forecast model, Germany, France and the United Kingdom are the top 3 highest country of EV electricity demand by 2030.

We notice that Greece, Iceland and Poland’s predicted data contains a lot of negative value. The reason could be that value of sales of EV in these countries are at the lowest end of our dataset and could be considered outlier. Since our model is built upon the aggregated electricity demand of the whole Europe, the accuracy of predictions would be low. Hence, they will be excluded from the visualization since negative values of electricity demand does not make sense. We will also exclude “Other_Europe” from the visualization since the publisher of the data did not specify which regions it covered.

fcastdf_final <- subset(fcastdf, select = -c(Greece, Iceland, Poland, Other_Europe))
fcastdf_final <- fcastdf_final %>% pivot_longer(2:14, names_to = "region", values_to = "value")
head(fcastdf_final)

Export the dataframe as .csv file for visualization in Tableau:

write_csv(fcastdf_final, "EVElectricityDemandProjected2021_2030.csv")

This is the Tableau visualization of the projected EV electricity demand dataset. Available filters includes a country filter and slider year filter. Value measure is in Gigawatt hour. Warmer colour suggests high demand and colder colour suggests low demand.

Share

This is the detailed document in R notebook, with every procedure and code documented and interactive Plotly visualization. The Google Slides version (no codes, only visualization, insights and recommendation) is available here. Interactive Tableau Viz worksheet is available here.

Act

Key findings

  • European EV market has experienced a consistent growth according to available data time frame (2010-2020).
  • From 2016 onwards, the total sales of EVs has seen accelerated growth year on year.
  • Germany, France and the UK seems to leads the EV sales figure and growth factor in Europe.
  • Battery Electric Vehicle (BEV) dominates the market with 54.7% of the total EV sales, with Plug-in Hybrid Electric Vehicle (PHEV) behind with 45.3%.
  • Fuel Cell Electric vehicle(FCEV) seems to be a niche choice for consumers as they only accounts for 0.07% of the total sales.
  • As of 2020, fast charger seems to be lacking as only 1 out of 10 publicly available chargers in Europe is a fast charger.
  • Public chargers in countries such as Netherlands, Italy and Belgium are mostly slow chargers, while Germany, Norway and Sweden has higher than average of EV per charger ratio.
  • On average, each BEV creates more electricity demand than PHEV.
  • According to our model, Germany, France and the UK will be the top 3 European countries of highest EV electricity demand by 2030.

Recommendations

  1. Germany, France and the UK are the prominent target for energy investments as they are the leaders in EV market growth in Europe.
  2. Battery Electric Vehicle (BEV) and Plug-in Hybrid Electric Vehicle (PHEV) dominates the market and Fuel Cell Electric vehicle(FCEV) remains to be niche and irrelevant as of 2020. Hence, investment projects in charging solutions should only consider EV charging instead of hydrogen or other fuel types.
  3. Fast chargers are rare and they are a crucial to enable long distance driving (Funke, Sprei, Gnann and Plötz, 2019). Consider invest in fast charging solution projects in these countries where they need the most: Netherlands, Italy and Belgium.
  4. Some countries lacks public charging all together: Germany, Norway and Sweden, therefore invest in any public charging project would suffice.
  5. Germany, France and the UK are the top 3 countries with highest projected EV electricity demand by 2030, therefore long term infrastructure projects in these regions could be considered.

Limitations of the data and the analysis

Based on the quality contraints of the datasets used, there are a number of limitations to be addressed:

  • Some regional data is missing from one or more datasets, making cross reference difficult as we had to exclude the missing region from the analysis.
  • The data point frequency in the datasets (annual) could compromise the accuracy of our analysis, especially to the ARIMA model.
  • Electricity demand dataset does not have data drilled down to each individual country. Instead, we modelled our multiple regression based on the figures of whole european EV electricity demand. This means that countries with extremely small/large values of EV sales would be prone to overfitting issues in the results of our EV electricity demand forecast.
  • There are no available data for private chargers, which could be an important factor in the supply and demand of charging stations. This analysis presumed public chargers accounts for most of the charging demand of EVs.

Reference

IEA. 2022. Global EV Data Explorer – Analysis - IEA. [online] Available at: https://www.iea.org/articles/global-ev-data-explorer [Accessed 6 January 2022]. All rights reserved.

Energy.gov. 2022. The History of the Electric Car. [online] Available at: https://www.energy.gov/articles/history-electric-car [Accessed 6 January 2022].

Popkostova, Y., 2022. EUROPE’S ENERGY CRISIS CONUNDRUM. [online] Iss.europa.eu. Available at: https://www.iss.europa.eu/sites/default/files/EUISSFiles/Brief_2_Energy%20Crisis_web.pdf [Accessed 2 March 2022].

Funke, S., Sprei, F., Gnann, T. and Plötz, P., 2019. How much charging infrastructure do electric vehicles need? A review of the evidence and international comparison. Transportation Research Part D: Transport and Environment, 77, pp.224-242.

LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgQ2FzZSBTdHVkeSAtIEV1cm9wZWFuIEVWIG1hcmtldCBhbmFseXNpcyINCkF1dGhvcjogIlJ1aXBlbmcgWXUiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIHRvYzogeWVzDQotLS0NCg0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KIyMjIFNjZW5hcmlvDQoNCk15IGNsaWVudCBpcyBhbiBpbmR1c3RyeS1sZWFkaW5nIGVuZXJneSBpbmZyYXN0cnVjdHVyZSBwcm92aWRlciBiYXNlZCBpbiBFdXJvcGUuIEZvbGxvd2luZyBvbnRvIHRoZSBlbmVyZ3kgY3Jpc2lzIHRoYXQgRXVyb3BlIGlzIGZhY2luZyAoUG9wa29zdG92YSwgMjAyMiksIHRoZXkgYXJlIGluIHRoZSBwcm9jZXNzIG9mIHNlY3VyaW5nIG1vcmUgZnVuZGluZyBmcm9tIGludmVzdG9ycywgYW5kIHRvIGludmVzdCBpbiBlbmVyZ3kgcHJvamVjdHMgaW4gRXVyb3BlIHRvIG1lZXQgdGhlIGV2ZXItZ3Jvd2luZyBlbGVjdHJpY2l0eSBkZW1hbmQuDQoNClRoZWlyIHJlbmV3YWJsZSBlbmVyZ3kgcHJvamVjdCB0ZWFtIGlzIGVhZ2VyIHRvIGRpc2NvdmVyIGluc2lnaHRzIGZyb20gdGhlIEVWIG1hcmtldC4gVGhleSB3b3VsZCBsaWtlIHRvIGtub3cgaWYgRVYgbWFya2V0IGlzIGltcGFjdGluZyB0aGUgZWxlY3RyaWNpdHkgZGVtYW5kIGluIEV1cm9wZSBhbmQgYnkgd2hhdCBtYWduaXR1ZGUsIHNvIHRoYXQgZGF0YS1kcml2ZW4gZGVjaXNpb25zIGNvdWxkIGJlIG1hZGUgYmFzZWQgb24gY29uY2x1c2lvbnMgZHJhd24gZnJvbSB0aGlzIGRhdGEgYW5hbHlzaXMgcHJvamVjdC4gT25jZSB3ZSBwcm9kdWNlIGFjdGlvbmFibGUgcmVjb21tZW5kYXRpb25zIHRvIHRoZW0sIHRoZXkgY291bGQgdGhlbiBjb25kdWN0IGZlYXNpYmlsaXR5IHN0dWR5IGJhc2VkIG9uIG91ciBjb25jbHVzaW9uIGFuZCBhZHZpc2UgdGhlaXIgYm9hcmQgYW5kIHN0YWtlaG9sZGVycy4NCg0KIyMgQXNrDQoNCiMjIyBEZWZpbmUgbWV0cmljcyBmcm9tIGJ1c2luZXNzIG9iamVjdGl2ZXMNCg0KRm9sbG93aW5nIGFuIGludGVydmlldyB3aXRoIG15IGNsaWVudCwgd2UgaGF2ZSBlc3RhYmxpc2hlZCB0aGVzZSBvYmplY3RpdmVzIGFzIGtleSB0YXNrczoNCg0KMS4JSWRlbnRpZnkgaGlzdG9yaWNhbCB0cmVuZHMgb2YgdGhlIEV1cm9wZWFuIEVWIG1hcmtldC4NCjIuCUlkZW50aWZ5IHNob3J0LXRlcm0gaW52ZXN0bWVudCBvcHBvcnR1bml0aWVzIGJhc2VkIG9uIGhpc3RvcmljYWwgYW5kIGN1cnJlbnQgZGVtYW5kLg0KMy4JSWRlbnRpZnkgbG9uZy10ZXJtIGludmVzdG1lbnQgb3Bwb3J0dW5pdGllcyBiYXNlZCBvbiBwcm9qZWN0ZWQgZnV0dXJlIGRlbWFuZHMuDQoNClRvIGFjY29tcGxpc2ggdGhlc2Ugb2JqZWN0aXZlcywgd2Ugd291bGQgYmUgcmVxdWlyZWQgdG8gcHJvY3VyZSByZWxldmFudCBhbmQgdXAgdG8gZGF0ZSBkYXRhIHNvdXJjZXMgdGhhdCBhcmUgcmVsYXRlZCB0byB0aGUgRVYgbWFya2V0IGluIEV1cm9wZSwgcHJvY2VzcyBhbmQgYW5hbHl6ZSB0aGVtIGluIG9yZGVyIHRvIGRyYXcgY29uY2x1c2lvbnMgYW5kIGJlIGFibGUgdG8gbWFrZSBhY3Rpb25hYmxlIGRhdGEtZHJpdmVuIHJlY29tbWVuZGF0aW9ucy4gV2UgbmVlZCB0byBxdWFudGlmeSB0aGUgZGVtYW5kLCB0cmVuZHMgYW5kIHRvIHZpc3VhbGl6ZSB0aGVzZSBpbiB0dXJuIHRvIGhlbHAgb3VyIHN0YWtlaG9sZGVycyB0byB1bmRlcnN0YW5kIHRoZSBpbnNpZ2h0cyBkaXNjb3ZlcmVkIGZyb20gb3VyIGFuYWx5c2lzLg0KDQojIyMgSWRlbnRpZnkga2V5IHN0YWtlaG9sZGVycw0KDQpUaGUgc3Rha2Vob2xkZXJzIG9mIHRoaXMgcHJvamVjdCB3b3VsZCBpbmNsdWRlOg0KDQoqIFByb2plY3QgbWFuYWdlciBvZiB0aGUgcmVuZXdhYmxlIGVuZXJneSBwcm9qZWN0IHRlYW0NCiogRGlyZWN0b3Igb2Ygc3RyYXRlZ2ljIGludmVzdG1lbnQgaW4gRW5lcmd5IGFuZCBUaGUgSGVhZCBvZiBFVSBwcm9qZWN0cw0KKiBCb2FyZCBvZiBkaXJlY3RvcnMNCg0KDQojIyBQcmVwYXJlDQoNCiMjIyBTb3VyY2UgYW5kIGNyZWRpYmlsaXR5IG9mIHRoZSBkYXRhDQoNClRoZSBbZGF0YXNldHNdKGh0dHBzOi8vd3d3LmllYS5vcmcvYXJ0aWNsZXMvZ2xvYmFsLWV2LWRhdGEtZXhwbG9yZXIpIHVzZWQgYXJlIHNvdXJjZWQgZnJvbSBJRUEgKEludGVybmF0aW9uYWwgRW5lcmd5IEFnZW5jeSksIGEgUGFyaXMtYmFzZWQgYXV0b25vbW91cyBpbnRlcmdvdmVybm1lbnRhbCBvcmdhbmlzYXRpb24gdGhhdCBzZXJ2ZXMgYXMgYSBwb2xpY3kgYWR2aXNlciBvbiBlbmVyZ3kgaXNzdWVzIGZvciAzMCBtZW1iZXIgY291bnRyaWVzLiBUaGUgZGF0YXNldHMgY29udGFpbiByZWNvcmRzIG9mIEVWIHNhbGVzLCBwdWJsaWNseSBhdmFpbGFibGUgRVYgY2hhcmdlcnMgYW5kIEVWIGVsZWN0cmljaXR5IGRlbWFuZCBzb3J0ZWQgYnkgY291bnRyaWVzIGFuZCByZWdpb25zIGJldHdlZW4gMjAxMC0yMDIwIChHbG9iYWwgRVYgRGF0YSBFeHBsb3JlciDigJMgQW5hbHlzaXMgLSBJRUEsIDIwMjIpLiBDb25zaWRlcmluZyB0aGF0IHRoZSBFViBtYXJrZXQgaXMgc3RpbGwgaW4gaXRzIGluZmFuY3kgYXMgdGhlIGZpcnN0IG1hc3MgcHJvZHVjZWQgRVYgaXMgb25seSByZWxlYXNlZCBpbiAxOTk3IChUaGUgSGlzdG9yeSBvZiB0aGUgRWxlY3RyaWMgQ2FyLCAyMDIyKSwgd2UgY2FuIGFzc3VtZSB0aGF0IHRoZSBkYXRhc2V0IHVzZWQgd291bGQgYmUgcmVsZXZhbnQgYW5kIHVwIHRvIGRhdGUgdG8gcmVmbGVjdCByZWNlbnQgbWFya2V0IHRyZW5kcy4NCg0KIyMjIExpY2Vuc2luZw0KVGhpcyBwcm9qZWN0IHdpbGwgbm90IGluY2x1ZGUgdGhlIG9yaWdpbmFsIGRhdGFzZXQgYXMgZG93bmxvYWRhYmxlIGZpbGUgYXMgdG8gYWRoZXJlIHRvIElFQSdzIHRlcm1zIGFuZCBjb25kaXRpb25zLCB3aGljaCBzdGF0ZXMgKmFueXRoaW5nIGdyZWF0ZXIgdGhhbiA1IChmaXZlKSBudW1lcmljYWwgZGF0YSBwb2ludHMgKGJ1dCBzdGlsbCBhbiBJbnN1YnN0YW50aWFsIEFtb3VudCkgZnJvbSB0aGUgTWF0ZXJpYWwgbXVzdCBub3QgYmUgbWFkZSBhdmFpbGFibGUgaW4gYSBzZXBhcmF0ZSBkb3dubG9hZGFibGUgZm9ybWF0IGFuZCBtdXN0IGJlIHByZXNlbnRlZCBlaXRoZXIgaW4gZ3JhcGhpY2FsIGZvcm1hdCBvciBhZ2dyZWdhdGVkIChpbiBzdWNoIGEgbWFubmVyIHRoYXQgdGhlIHJlYWRlciBjYW5ub3QgcmV2ZXJzZSBlbmdpbmVlciBvciBleHRyYWN0IHRoZSBvcmlnaW5hbCB1bmRlcmx5aW5nIG51bWVyaWNhbCBkYXRhKS4qDQoNClRoaXMgYWxzbyBtZWFucyB0aGF0IHRoZSBhbmFseXNpcyBwcm9jZXNzIHdpbGwgb25seSBpbmNsdWRlIHRoZSBzdW1tYXJ5IG9yIGhlYWQgb2YgYSBkYXRhc2V0LCB3aGVyZSBhcHByb3ByaWF0ZS4NCg0KIyMjIEhvdyBpcyB0aGUgZGF0YSBvcmdhbml6ZWQ/DQoNClRoZSBkYXRhc2V0cyB1c2VkIGFyZSB0aHJlZSBzZXBlcmF0ZSBjc3YgZmlsZXMgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgbmFtaW5nIGNvbnZlbnRpb24gdXNlZCB0byBuYW1lIGRhdGFmcmFtZXM6DQoNCiogSUVBLUVWLWRhdGFFViBzYWxlc0NhcnNIaXN0b3JpY2FsLmNzdiAoRVZzYWxlcykNCg0KVGhpcyBkYXRhc2V0IGNvbnRhaW5zIHRoZSBzYWxlcyBkYXRhIG9mIEVsZWN0cmljIFZlaGljbGVzIGZyb20gMjAxMCB0byAyMDIwIGluIGRpZmZlcmVudCBjb3VudHJpZXMgYW5kIHJlZ2lvbnMuIEtleSBpbmZvcm1hdGlvbiBpbmNsdWRlczogVHlwZXMgb2YgRVYuDQoNCiogSUVBLUVWLWRhdGFFViBjaGFyZ2Vyc0hpc3RvcmljYWwuY3N2IChFVmNoYXJnZXJzKQ0KDQpUaGlzIGRhdGFzZXQgY29udGFpbnMgdGhlIHB1YmxpY2x5IGF2YWlsYWJsZSBjaGFyZ2VycyBvZiBFbGVjdHJpYyBWZWhpY2xlcyBmcm9tIDIwMTAgdG8gMjAyMCBpbiBkaWZmZXJlbnQgY291bnRyaWVzIGFuZCByZWdpb25zLiBLZXkgaW5mb3JtYXRpb24gaW5jbHVkZXM6IFR5cGVzIG9mIGNoYXJnZXJzLg0KDQoqIElFQS1FVi1kYXRhRWxlY3RyaWNpdHkgZGVtYW5kQ2Fyc0hpc3RvcmljYWxfTW9Nby5jc3YgKEVWZGVtYW5kKQ0KDQpUaGlzIGRhdGFzZXQgY29udGFpbnMgdGhlIGVsZWN0cmljaXR5IGRlbWFuZCBvZiBFbGVjdHJpYyBWZWhpY2xlcyBmcm9tIDIwMTUgdG8gMjAyMC4gS2V5IGluZm9ybWF0aW9uIGluY2x1ZGVzOiBFbGVjdHJpY2l0eSBkZW1hbmQgYmFzZWQgb24gdGhlIHR5cGUgb2YgRVYuDQoNCg0KV2Ugd2lsbCBsb2FkIHRoZW0gaW50byBkYXRhZnJhbWVzIHRvIGV4YW1pbmUgaG93IHRoZXkgYXJlIG9yZ2FuaXplZC4NCg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIsIHdhcm4uY29uZmxpY3RzID0gRkFMU0UpDQpvcHRpb25zKGRwbHlyLnN1bW1hcmlzZS5pbmZvcm0gPSBGQUxTRSkNCmxpYnJhcnkodGlkeXZlcnNlLCB3YXJuLmNvbmZsaWN0cyA9IEZBTFNFKQ0KDQpFVnNhbGVzIDwtIHJlYWQuY3N2KHVybCgiaHR0cHM6Ly9hcGkuaWVhLm9yZy9ldnMvP3BhcmFtZXRlcj1FViUyMHNhbGVzJm1vZGU9Q2FycyZjYXRlZ29yeT1IaXN0b3JpY2FsJmNzdj10cnVlIikpDQpFVmNoYXJnZXJzIDwtIHJlYWQuY3N2KHVybCgiaHR0cHM6Ly9hcGkuaWVhLm9yZy9ldnMvP3BhcmFtZXRlcj1FViUyMGNoYXJnZXJzJmNhdGVnb3J5PUhpc3RvcmljYWwmY3N2PXRydWUiKSkNCkVWZGVtYW5kIDwtIHJlYWQuY3N2KHVybCgiaHR0cHM6Ly9hcGkuaWVhLm9yZy9ldnMvP3BhcmFtZXRlcj1FbGVjdHJpY2l0eSUyMGRlbWFuZCZtb2RlPUNhcnMmY2F0ZWdvcnk9SGlzdG9yaWNhbF9Nb01vJmNzdj10cnVlIikpDQoNCmhlYWQoRVZzYWxlcykNCmhlYWQoRVZjaGFyZ2VycykNCmhlYWQoRVZkZW1hbmQpDQpgYGANCg0KDQojIyMgUG90ZW50aWFsIHByb2JsZW1zIHdpdGggdGhlIGRhdGENCg0KVXBvbiB0aGUgZXhhbWluYXRpb24gb2YgZGF0YSBhYm92ZSwgd2UgcmVjb2duaXplIHRoZSBwb3RlbnRpYWwgcHJvYmxlbXMgYmVsb3c6DQoNCiogc29tZSBjb2x1bW5zIGluIGVhY2ggZGF0YXNldCBpcyByZWR1bmRhbnQgKGkuZS4gbm90IHVzZWZ1bCB0byBhbmFseXplIHRoZW0pDQoqIERhdGFzZXRzIGNvdWxkIGNvbnRhaW4gZGlmZmVyZW50IHRpbWUgcmFuZ2UgKEVWIGVsZWN0cmljaXR5IGRlbWFuZCBkYXRhc2V0IHNlZW1zIHRvIHN0YXJ0IGZyb20gMjAxNSwgaW5zdGVhZCBvZiAyMDEwKQ0KKiBzb21lIG9mIHRoZSBmaWVsZHMgY291bGQgYmUgbWlzc2luZyAoaS5lLiBjb250YWlucyBubyBkYXRhKQ0KKiBUaGUgZGVmYXVsdCBkYXRhdHlwZSBhc3NpZ25lZCBieSBSIGNvdWxkIGJlIGluYXBwcm9wcmlhdGUgZm9yIHB1cnBvc2Ugb2YgYW5hbHlzaXMNCg0KV2Ugd2lsbCBwYXkgYXR0ZW50aW9uIGFuZCBhdHRlbXB0IHRvIG92ZXJjb21lIHRoZXNlIHByb2JsZW1zIGluIHRoZSBuZXh0IHN0ZXAgLSBQcm9jZXNzDQoNCiMjIFByb2Nlc3MNCg0KIyMjIERhdGEgY2xlYW5pbmcgYW5kIHByZS1wcm9jZXNzaW5nDQoNCkZpcnN0LCBsZXQncyBleHBsb3JlIHdoaWNoIHJlZ2lvbnMgYXJlIGluY2x1ZGVkIGluIG91ciBFViBzYWxlcyBkYXRhc2V0LiBUbyBkbyB0aGlzLCB3ZSB1c2UgdGhlIHVuaXF1ZSgpIGZ1bmN0aW9uIG9uICJyZWdpb24iIGFuZCAieWVhciIgdmFyaWFibGUgdG8gYWNjZXNzIG9ubHkgdGhlIHVuaXF1ZSB2YWx1ZXMuDQpgYGB7cn0NCnVuaXF1ZShFVnNhbGVzW2MoInJlZ2lvbiIpXSkNCmBgYA0KYGBge3J9DQp1bmlxdWUoRVZzYWxlc1tjKCJ5ZWFyIildKQ0KYGBgDQpIZXJlIHdlIGhhdmUgMTEgeWVhcnMgb2YgZGF0YSB0byBsb29rIGF0LCBmcm9tIDIwMTAgdG8gMjAyMC4NCg0KTmV4dCwgd2UgdGFrZSBhIGxvb2sgYXQgc29tZSBzZWVtaW5nbHkgaXJyZWxldmFudCBjb2x1bW5zLiBXZSBjb3VsZCB0aGVuIGRyb3AgdGhlc2UgY29sdW1ucyBpZiB0aGV5IGFyZSBpbmRlZWQgcmVkdW5kYW50IGFuZCBwcm92ZXMgdG8gYmUgbm90IHVzZWZ1bCBmb3IgdGhlIHB1cnBvc2Ugb2Ygb3VyIGRhdGEgYW5hbHlzaXMuDQpgYGB7cn0NCnVuaXF1ZShFVnNhbGVzW2MoImNhdGVnb3J5IildKQ0KdW5pcXVlKEVWc2FsZXNbYygicGFyYW1ldGVyIildKQ0KdW5pcXVlKEVWc2FsZXNbYygibW9kZSIpXSkNCnVuaXF1ZShFVnNhbGVzW2MoInVuaXQiKV0pDQpgYGANCg0KVGhlc2UgNCBjb2x1bW5zIG9ubHkgY29udGFpbnMgc2luZ3VsYXIgY2F0ZWdvcmljYWwgZGF0YSB3aGljaCB3b3VsZCBub3QgYmUgdXNlZnVsIGZvciBvdXIgYW5hbHlzaXMsIGhlbmNlIHdlIHdpbGwgZHJvcCB0aGVzZSBjb2x1bW5zIGZyb20gb3VyIGRhdGFmcmFtZXMuDQpgYGB7cn0NCkVWc2FsZXNfY2xlYW5lZCA8LSBzdWJzZXQoRVZzYWxlcywgc2VsZWN0ID0gLWMoY2F0ZWdvcnksIHBhcmFtZXRlciwgbW9kZSwgdW5pdCkpDQpnbGltcHNlKEVWc2FsZXNfY2xlYW5lZCkNCmBgYA0KTm93IG91ciBFVnNhbGVzIGRhdGFmcmFtZSBvbmx5IGNvbnRhaW5zIDQgdXNlZnVsIGF0dHJpYnV0ZXM6IHJlZ2lvbiwgcG93ZXJ0cmFpbiwgeWVhciBhbmQgdmFsdWUuIFdlIHdpbGwgY29udGludWUgd2l0aCB0aGUgbmV4dCBzdGVwcyBvZiBkYXRhIHByb2Nlc3NpbmcuDQoNCkRlc3BpdGUgdGhhdCB0aGUgZGF0YSBpcyBzb3VyY2VkIGZyb20gYSBjcmVkaWJsZSBvcmdhbml6YXRpb24gYW5kIG1vc3QgbGlrZWx5IHRvIGJlIGNsZWFuZWQgYWxyZWFkeSwgd2Ugd2lsbCBzdGlsbCBzZWFyY2ggZm9yIGFueSBwb3RlbnRpYWwgbnVsbCBvciBlcnJvbmVvdXMgdmFsdWUgaW4gb3JkZXIgdG8gbWFrZSBzdXJlIHdlIGFyZSB1c2luZyBjbGVhbiBhbmQgY29ycmVjdCBkYXRhLg0KYGBge3J9DQpFVnNhbGVzX2NsZWFuZWQgPC0gZHJvcF9uYShFVnNhbGVzX2NsZWFuZWQpDQpnbGltcHNlKEVWc2FsZXNfY2xlYW5lZCkNCmBgYA0KDQpIZXJlIHdlIG5vdGljZSB0aGF0IHRoZSB2YWx1ZSBkYXRhdHlwZSBpcyBkb3VibGUuIFNpbmNlIHRoZXJlIGNhbid0IGJlICcwLjI5Nycgb3IgaGFsZiBvZiBhIGNhciwgd2Ugd2lsbCBjb252ZXJ0IHRoZSBkYXRhdHlwZSBvbiAidmFsdWUiIGNvbHVtbiB0byBpbnRlZ2VyIHRvIGVuZm9yY2UgZGF0YSBpbnRlZ3JpdHkuDQpgYGB7cn0NCkVWc2FsZXNfY2xlYW5lZCA8LSB0cmFuc2Zvcm0oRVZzYWxlc19jbGVhbmVkLCB2YWx1ZSA9IGFzLmludGVnZXIodmFsdWUpKQ0KYGBgDQoNClNpbmNlIHdlIG9ubHkgd2FudCB0byBleHBsb3JlIHRoZSBFViBtYXJrZXQgdHJlbmRzIGluIEV1cm9wZSwgd2UgY291bGQgZXhjbHVkZSByZXN0IG9mIHJlZ2lvbnMgb3V0c2lkZSB0aGUgRXVyb3BlYW4gY29udGluZW50LiBIZXJlIHdlIGNyZWF0ZSBhIHZlY3RvciBvZiBFdXJvcGVhbiBjb3VudHJpZXMgZnJvbSB0aGUgInJlZ2lvbiIgZGF0YSB3ZSBzYXcgYWJvdmUsIGFuZCB0aGVuIGRyb3BwaW5nIHJvd3Mgd2hpY2ggcmVnaW9ucyBkb2VzIG5vdCBtYXRjaC4NCmBgYHtyfQ0KbGlzdF9vZl9FVV9jb3VudHJpZXMgPC0gYygiQmVsZ2l1bSIsICJEZW5tYXJrIiwgIkZpbmxhbmQiLCAiRnJhbmNlIiwgIkdlcm1hbnkiLCAiR3JlZWNlIiwgIkljZWxhbmQiLCAiSXRhbHkiLCAiTmV0aGVybGFuZHMiLCAiTm9yd2F5IiwgIk90aGVyIEV1cm9wZSIsICJQb2xhbmQiLCAiUG9ydHVnYWwiLCAiU3BhaW4iLCAiU3dlZGVuIiwgIlN3aXR6ZXJsYW5kIiwgIlVuaXRlZCBLaW5nZG9tIikNCkVWc2FsZXNfY2xlYW5lZCA8LSBzdWJzZXQoRVZzYWxlc19jbGVhbmVkLCByZWdpb24gJWluJSBsaXN0X29mX0VVX2NvdW50cmllcykNCmBgYA0KDQpIZXJlIGlzIGEgZ2xpbXBzZSBvZiBvdXIgY2xlYW5lZCBFVnNhbGVzIGRhdGFmcmFtZToNCmBgYHtyfQ0KaGVhZChFVnNhbGVzX2NsZWFuZWQpDQpgYGANCg0KTmV4dCwgd2Ugd2lsbCBhcHBseSB0aGUgc2FtZSBkYXRhIGNsZWFuaW5nIHRlY2huaXF1ZXMgdG8gdGhlIG90aGVyIHR3byBkYXRhc2V0cyB3ZSBoYXZlLg0KYGBge3J9DQp1bmlxdWUoRVZjaGFyZ2Vyc1tjKCJjYXRlZ29yeSIpXSkNCnVuaXF1ZShFVmNoYXJnZXJzW2MoInBhcmFtZXRlciIpXSkNCnVuaXF1ZShFVmNoYXJnZXJzW2MoIm1vZGUiKV0pDQp1bmlxdWUoRVZjaGFyZ2Vyc1tjKCJ1bml0IildKQ0KdW5pcXVlKEVWY2hhcmdlcnNbYygicG93ZXJ0cmFpbiIpXSkNCnVuaXF1ZShFVmNoYXJnZXJzW2MoInllYXIiKV0pDQpgYGANCg0KV2UncmUga2VlcGluZyBwb3dlcnRyYWluIGFuZCBkcm9wcGluZyB0aGUgb3RoZXIgNCBzaW5jZSB0aGV5IGNvbnRhaW4gbm8gZGF0YSBvciB1c2VsZXNzIGRhdGEuDQpgYGB7cn0NCkVWY2hhcmdlcnNfY2xlYW5lZCA8LSBzdWJzZXQoRVZjaGFyZ2Vycywgc2VsZWN0ID0gLWMoY2F0ZWdvcnksIHBhcmFtZXRlciwgbW9kZSwgdW5pdCkpDQpFVmNoYXJnZXJzX2NsZWFuZWQgPC0gZHJvcF9uYShFVmNoYXJnZXJzX2NsZWFuZWQpDQpFVmNoYXJnZXJzX2NsZWFuZWQgPC0gdHJhbnNmb3JtKEVWY2hhcmdlcnNfY2xlYW5lZCwgdmFsdWUgPSBhcy5pbnRlZ2VyKHZhbHVlKSkNCkVWY2hhcmdlcnNfY2xlYW5lZCA8LSBzdWJzZXQoRVZjaGFyZ2Vyc19jbGVhbmVkLCByZWdpb24gJWluJSBsaXN0X29mX0VVX2NvdW50cmllcykNCmhlYWQoYXJyYW5nZShFVmNoYXJnZXJzX2NsZWFuZWQsIHllYXIpKQ0KYGBgDQoNClByaW50aW5nIHVuaXF1ZSB2YWx1ZXMgaW4gRVZkZW1hbmQgZGF0YWZyYW1lLg0KYGBge3J9DQp1bmlxdWUoRVZkZW1hbmRbYygicmVnaW9uIildKQ0KdW5pcXVlKEVWZGVtYW5kW2MoImNhdGVnb3J5IildKQ0KdW5pcXVlKEVWZGVtYW5kW2MoInBhcmFtZXRlciIpXSkNCnVuaXF1ZShFVmRlbWFuZFtjKCJtb2RlIildKQ0KdW5pcXVlKEVWZGVtYW5kW2MoInVuaXQiKV0pDQp1bmlxdWUoRVZkZW1hbmRbYygicG93ZXJ0cmFpbiIpXSkNCnVuaXF1ZShFVmRlbWFuZFtjKCJ5ZWFyIildKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIGRyb3AgY2F0ZWdvcnksIHBhcmFtZXRlciwgbW9kZSBhbmQgdW5pdCBhdHRyaWJ1dGVzIGFnYWluLCB0aGVuIGRyb3BwaW5nIHJvd3MgY29udGFpbmluZyBudWxsIHZhbHVlcyBiZWZvcmUgb25seSBpbmNsdWRpbmcgRXVyb3BlIHJlZ2lvbiBkYXRhLg0KYGBge3J9DQpFVmRlbWFuZF9jbGVhbmVkIDwtIHN1YnNldChFVmRlbWFuZCwgc2VsZWN0ID0gLWMoY2F0ZWdvcnksIHBhcmFtZXRlciwgbW9kZSwgdW5pdCkpDQpFVmRlbWFuZF9jbGVhbmVkIDwtIGRyb3BfbmEoRVZkZW1hbmRfY2xlYW5lZCkNCkVWZGVtYW5kX2NsZWFuZWQgPC0gc3Vic2V0KEVWZGVtYW5kX2NsZWFuZWQsIHJlZ2lvbiA9PSJFdXJvcGUiKQ0KaGVhZChhcnJhbmdlKEVWZGVtYW5kX2NsZWFuZWQsIHllYXIpKQ0KYGBgDQoNCiMjIyBQcm9jZWR1cmVzIG9mIGRhdGEgY2xlYW5pbmcgYW5kIHByb2Nlc3NpbmcNCg0KKiBBbGwgZGF0YXNldCB3ZXJlIGV4YW1pbmVkIGJ5IGxvb2tpbmcgYXQgdW5pcXVlIHZhbHVlcyBvZiBlYWNoIHZhcmlhYmxlLg0KKiBDb2x1bW5zIHRoYXQgYXJlIG5vdCB1c2VmdWwgdG8gb3VyIGFuYWx5c2lzIG9iamVjdGl2ZSB3ZXJlIGRyb3BwZWQuDQoqIE9ic2VydmF0aW9ucyB3aXRoIGFueSBlbXB0eSB2YWx1ZXMgd2VyZSBkcm9wcGVkIChpZiB0aGVyZSB3ZXJlIGFueSkuDQoqIFJvd3Mgd2hlcmUgcmVnaW9uIGlzIG5vdCBwYXJ0IG9mIEV1cm9wZSB3ZXJlIGRyb3BwZWQuDQoqIERhdGEgdHlwZXMgd2VyZSBhbWVuZGVkIHdoZXJlIGFwcHJvcHJpYXRlIChpLmUuIEVWIHNhbGVzIHNob3VsZCBiZSBhIHdob2xlIG51bWJlciBhbmQgZWxlY3RyaWNpdHkgZGVtYW5kIGNvdWxkIGJlIGEgZmxvYXQuKQ0KDQpIZXJlIGlzIGEgc3VtbWFyeSBvZiBlYWNoIGRhdGFmcmFtZSBiZWZvcmUgdGhlIHByb2NlZHVyZXMgYW5kIGFmdGVyOg0KDQoqKkVWc2FsZXM6IDcwNCByb3dzLCA4IHZhcmlhYmxlcy4qKg0KDQoqKkVWc2FsZXNfY2xlYW5lZDogNDIyIHJvd3MsIDQgdmFyaWFibGVzLioqDQoNCioqRVZjaGFyZ2VyczogNDY0IHJvd3MsIDggdmFyaWFibGVzLioqDQoNCioqRVZjaGFyZ2Vyc19jbGVhbmVkOiAyODQgcm93cywgNCB2YXJpYWJsZXMuKioNCg0KKipFVmRlbWFuZDogNTYgcm93cywgOCB2YXJpYWJsZXMuKioNCg0KKipFVmRlbWFuZF9jbGVhbmVkOiAgMTIgcm93cywgNCB2YXJpYWJsZXMuKioNCg0KIyMgQW5hbHl6ZQ0KDQppbXBvcnQgb3VyIGxpYnJhcmllcyBnZ3Bsb3QyLCB2aXJpZGlzIGFuZCBwbG90bHkgZm9yIHZpc3VhbGl6YXRpb24uDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90Miwgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkNCmxpYnJhcnkodmlyaWRpcywgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkNCmxpYnJhcnkocGxvdGx5LCB3YXJuLmNvbmZsaWN0cyA9IEZBTFNFKQ0KYGBgDQoNCkZpcnN0LCB3ZSBwcm9kdWNlIGEgc2ltcGxlIGxpbmUgcGxvdCBvZiB0aGUgdG90YWwgRVYgc2FsZXMgb2YgRXVyb3BlYW4gY291bnRyaWVzIGJldHdlZW4gMjAxMCBhbmQgMjAyMC4NCg0KYGBge3J9DQpvcHRpb25zKHNjaXBlbj0xMDAwKQ0KcGx0X1RvdGFsRVZzYWxlcyA8LSBkYXRhLmZyYW1lKEVWc2FsZXNfY2xlYW5lZCAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcml6ZSgiVG90YWxFVnNhbGVzIiA9IHN1bSh2YWx1ZSkpKQ0KcGx0X1RvdGFsRVZzYWxlcyA8LSBwbHRfVG90YWxFVnNhbGVzICU+JSBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1Ub3RhbEVWc2FsZXMpKSArDQogIGdlb21fbGluZSgpKw0KICBsYWJzKHggPSAiWWVhciIsIA0KICAgICAgIHkgPSAiTnVtYmVycyBvZiBFViBzb2xkIiwNCiAgICAgICB0aXRsZSA9ICJFViBzYWxlcyBvZiBFdXJvcGVhbiBDb3VudHJpZXMgQ29tYmluZWQgKDIwMTAtMjAyMCkiKSsNCiAgdGhlbWVfY2xhc3NpYygpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMjAxMDoyMDIwKQ0KZ2dwbG90bHkocGx0X1RvdGFsRVZzYWxlcywgd2lkdGg9ODAwKQ0KYGBgDQojIyMgSW5zaWdodCAxDQpGcm9tIHRoZSBsaW5lIHBsb3QsIHdlIGNhbiBzZWUgdGhhdCB0aGUgVG90YWwgRVYgc2FsZXMgaW4gRXVyb3BlIGhhcyBiZWVuIGNvbnNpc3RlbnRseSBncm93aW5nIG92ZXIgdGhlIHRpbWUgcGVyaW9kIGJldHdlZW4gMjAxMCBhbmQgMjAyMCwgd2l0aCBhY2NlbGVyYXRlZCBncm93dGggZXNwZWNpYWxseSBkdXJpbmcgMjAxNiB0byAyMDIwLg0KTmV4dCwgd2UgbG9vayBkZWVwZXIgaW50byB0aGUgZGF0YSBieSBwbG90dGluZyBhbm51YWwgc2FsZXMgZGF0YSBzb3J0ZWQgYnkgcmVnaW9uKGNvdW50cmllcykuDQoNCmBgYHtyfQ0KcGx0X0VWc2FsZXNCeVJlZ2lvbiA8LSBkYXRhLmZyYW1lKEVWc2FsZXNfY2xlYW5lZCAlPiUgZ3JvdXBfYnkocmVnaW9uLHllYXIpICU+JSBzdW1tYXJpemUoc3VtKHZhbHVlKSkpDQoNCnBsdF9FVnNhbGVzQnlSZWdpb24gPC0gcGx0X0VWc2FsZXNCeVJlZ2lvbiAlPiUgZ2dwbG90KGFlcyh4PXllYXIsIHk9c3VtLnZhbHVlLiwgY29sb3IgPXJlZ2lvbikpICsNCiAgZ2VvbV9saW5lKCkrDQogIGxhYnMoeCA9ICJZZWFyIiwgDQogICAgICAgeSA9ICJOdW1iZXJzIG9mIEVWIHNvbGQiLA0KICAgICAgIHRpdGxlID0gIkVWIHNhbGVzIGJ5IEV1cm9wZWFuIENvdW50cmllcyAoMjAxMC0yMDIwKSIpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAyMDEwOjIwMjApDQpnZ3Bsb3RseShwbHRfRVZzYWxlc0J5UmVnaW9uLCB3aWR0aD04MDApDQpgYGANCiMjIyBJbnNpZ2h0IDINCldlIGNhbiBzZWUgdGhhdCBHZXJtYW55LCBGcmFuY2UgYW5kIHRoZSBVbml0ZWQgS2luZ2RvbSBhcmUgdGhlIGxlYWRpbmcgdG9wIDMgY291bnRyaWVzIGluIHRoZSBzYWxlcyBvZiBFViBpbiBFdXJvcGUuIEFsc28sIHRoaXMgZ3JvdXAgb2YgMyBhcHBlYXJzIHRvIGhhdmUgdGhlIGhpZ2hlc3QgcmF0ZSBvZiBncm93dGhzIHRvbywgd2Ugd2lsbCBpbnZlc3RpZ2F0ZSB0aGlzIGZ1cnRoZXIgd2l0aCBhIGRpZmZlcmVudCBkYXRhc2V0IGxhdGVyIG9uLg0KDQpOZXh0LCB3ZSBsb29rIGF0IHRoZSBzYWxlcyBkaWZmZXJlbnQgdHlwZXMgb2YgRVZzIGluIHRlcm1zIG9mIHBvd2VydHJhaW4uDQpgYGB7cn0NCnBsdF9FVnNhbGVzQnlQb3dlcnRyYWluIDwtIGRhdGEuZnJhbWUoRVZzYWxlc19jbGVhbmVkICU+JSBncm91cF9ieShwb3dlcnRyYWluKSAlPiUgc3VtbWFyaXplKHN1bSh2YWx1ZSkpKQ0KcGx0X0VWc2FsZXNCeVBvd2VydHJhaW4gPC0gcGx0X0VWc2FsZXNCeVBvd2VydHJhaW4gJT4lIGdncGxvdChhZXMoeD0iIiwgeT1zdW0udmFsdWUuLCBmaWxsPXBvd2VydHJhaW4pKSArDQogICAgZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IiwgY29sb3I9IndoaXRlIikrDQogIGxhYnModGl0bGUgPSAiVG90YWwgRVYgc2FsZXMgYnkgcG93ZXJ0cmFpbiB0eXBlcywgXG5FdXJvcGUgQ29tYmluZWQgKDIwMTAtMjAyMCkiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygic2xhdGVibHVlMSIsICJnb2xkIiwgInNpZW5uYTMiKSkrDQogIHRoZW1lX3ZvaWQoKQ0KcGx0X0VWc2FsZXNCeVBvd2VydHJhaW4gPC0gcGx0X0VWc2FsZXNCeVBvd2VydHJhaW4gKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJTAuMmYlJSIsIHJvdW5kKChzdW0udmFsdWUuIC8gc3VtKHN1bS52YWx1ZS4pKjEwMCksIGRpZ2l0cyA9IDIpKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikNCnBsdF9FVnNhbGVzQnlQb3dlcnRyYWluDQpgYGANCiMjIyBJbnNpZ2h0IDMNCk91dCBvZiAzIHR5cGVzIG9mIEVWcyBpbmNsdWRlZCBpbiB0aGUgRVYgc2FsZXMgZGF0YXNldCwgQmF0dGVyeSBFbGVjdHJpYyBWZWhpY2xlKEJFVikgaXMgdGhlIG1vc3QgcG9wdWxhciBjaG9pY2UgYWNjb3VudGVkIGZvciA1NC42OCUgb2YgdGhlIHRvdGFsIHNhbGVzLiBQbHVnLWluIEh5YnJpZCBFbGVjdHJpYyBWZWhpY2xlcyhQSEVWKSBmb2xsb3dzIHNsaWdodGx5IGJlaGluZCB3aXRoIDQ1LjI1JS4NCkZpbmFsbHksIEZ1ZWwgQ2VsbCBFbGVjdHJpYyB2ZWhpY2xlKEZDRVYpIHNlZW1zIHRvIGJlIGEgbmljaGUgY2hvaWNlIGZvciBjb25zdW1lcnMgYXMgdGhleSBvbmx5IGFjY291bnRzIGZvciAwLjA3JSBvZiB0aGUgdG90YWwgc2FsZXMgYmV0d2VlbiAyMDEwLTIwMjAuDQoNCk5leHQsIHdlIHdpbGwgbG9vayBhdCBwdWJsaWMgY2hhcmdlciB0eXBlcyBpbiBhbGwgb2YgRXVyb3BlLg0KYGBge3J9DQpwbHRfRVZjaGFyZ2Vyc1BjdCA8LSBkYXRhLmZyYW1lKEVWY2hhcmdlcnNfY2xlYW5lZCAlPiUgZ3JvdXBfYnkocG93ZXJ0cmFpbikgJT4lIHN1bW1hcml6ZShzdW0odmFsdWUpKSkNCnBsdF9FVmNoYXJnZXJzUGN0IDwtIHBsdF9FVmNoYXJnZXJzUGN0ICU+JSBnZ3Bsb3QoYWVzKHg9IiIsIHk9c3VtLnZhbHVlLiwgZmlsbD1wb3dlcnRyYWluKSkgKw0KICAgIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yPSJ3aGl0ZSIpKw0KICBsYWJzKHRpdGxlID0gIkNoYXJnZXIgdHlwZXMgaW4gcGVyY2VudGFnZSwgRXVyb3BlIENvbWJpbmVkICgyMDIwKSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJzbGF0ZWJsdWUxIiwgImdvbGQiLCAic2llbm5hMyIpKSsNCiAgdGhlbWVfdm9pZCgpDQpwbHRfRVZjaGFyZ2Vyc1BjdCA8LSBwbHRfRVZjaGFyZ2Vyc1BjdCArIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlMC4yZiUlIiwgcm91bmQoKHN1bS52YWx1ZS4gLyBzdW0oc3VtLnZhbHVlLikqMTAwKSwgZGlnaXRzID0gMikpKSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKQ0KcGx0X0VWY2hhcmdlcnNQY3QNCmBgYA0KIyMjIEluc2lnaHQgNA0KQXMgb2YgMjAyMCwgcm91Z2hseSBvbmx5IDEgb3V0IG9mIDEwIHB1YmxpY2x5IGF2YWlsYWJsZSBjaGFyZ2VycyBpbiBFdXJvcGUgaXMgYSBmYXN0IGNoYXJnZXIuIA0KDQpOb3csIHdlIHBsb3QgdGhlIG51bWJlciBvZiBjaGFyZ2VycyB2cy4gY291bnRyaWVzIGluIDIwMjAuDQoNCmBgYHtyfQ0KcGx0X0VWY2hhcmdlcnNCeVJlZ2lvbiA8LSBnZ3Bsb3QoZGF0YSA9IEVWY2hhcmdlcnNfY2xlYW5lZCAlPiUgZmlsdGVyKHllYXI9PSIyMDIwIiksIGFlcyh5PXJlb3JkZXIocmVnaW9uLCB2YWx1ZSksIHg9dmFsdWUsIGZpbGwgPXBvd2VydHJhaW4pKSArDQogICAgZ2VvbV9jb2woKSsNCiAgbGFicyh4ID0gIk51bWJlcnMgb2YgQ2hhcmdlcnMiLCANCiAgICAgICB5ID0gIkNvdW50cmllcyIsDQogICAgICAgdGl0bGUgPSAiUHVibGljIGF2YWlsYWJsZSBFViBjaGFyZ2VycyBieSBFdXJvcGVhbiBDb3VudHJpZXMgKDIwMjApIikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInNsYXRlYmx1ZTEiLCAiZ29sZCIpKQ0KZ2dwbG90bHkocGx0X0VWY2hhcmdlcnNCeVJlZ2lvbiwgd2lkdGg9ODAwKQ0KYGBgDQojIyMgSW5zaWdodCA1DQpDb3VudHJpZXMgc3VjaCBhcyBOZXRoZXJsYW5kcywgSXRhbHkgYW5kIEJlbGdpdW0gaGFzIGEgc21hbGwgZmFzdCBjaGFyZ2VycyB0byBzbG93IGNoYXJnZXJzIHJhdGlvLg0KDQoNCk5leHQsIHdlIGV4cGxvcmUgaG93IG1hbnkgcHVibGljIGNoYXJnZXJzIGFyZSBhdmFpbGFibGUgcGVyIEVWIGluIGRpZmZlcmVudCBFdXJvcGVhbiBjb3VudHJpZXMuDQoNCg0KYGBge3J9DQpkZjEgPC0gRVZjaGFyZ2Vyc19jbGVhbmVkICU+JSBmaWx0ZXIoeWVhcj09IjIwMjAiKSAlPiUgZ3JvdXBfYnkocmVnaW9uKSAlPiUgc3VtbWFyaXplKCJFVmNoYXJnZXJzIiA9IHN1bSh2YWx1ZSkpDQpkZjIgPC0gRVZzYWxlc19jbGVhbmVkICU+JSBncm91cF9ieShyZWdpb24pICU+JSBzdW1tYXJpemUoIkVWc2FsZXNjb21iaW5lZCIgPSBzdW0odmFsdWUpKQ0KZGYzIDwtIG1lcmdlKGRmMSwgZGYyLCBieT0icmVnaW9uIikNCmRmMyRFVnNQZXJDaGFyZ2VyIDwtIGRmMyRFVnNhbGVzY29tYmluZWQgLyBkZjMkRVZjaGFyZ2Vycw0KaGVhZChkZjMpDQpgYGANCg0KdmlzdWFsemluZyB0aGUgZGF0YSwgZGFzaGVkIGxpbmUgaXMgdGhlIG1lYW4gdmFsdWUgb2YgaG93IG1hbnkgRVZzIGFyZSB0aGVyZSBwZXIgcHVibGljbHkgYXZhaWxhYmxlIGNoYXJnZXIuDQoNCmBgYHtyfQ0KcGx0RVZ2c0NoYXJnZXJzIDwtIGdncGxvdChkZjMsIGFlcyh5PXJlb3JkZXIocmVnaW9uLCBFVnNhbGVzY29tYmluZWQpLCB4PUVWc1BlckNoYXJnZXIsIGZpbGw9IkVWc1BlckNoYXJnZXIiKSkgKw0KICAgIGdlb21fY29sKCkrDQogIGxhYnMoeCA9ICJOby4gb2YgRVZzIiwgDQogICAgICAgeSA9ICJDb3VudHJpZXMgcmFua2VkIGluIG9yZGVyIG9mIE5vLiBvZiBFViBTYWxlcyIsDQogICAgICAgdGl0bGUgPSAiTnVtYmVycyBvZiBFVnMgUGVyIENoYXJnZXIgKFB1YmxpY2x5IEF2YWlsYWJsZSBTbG93ICsgRmFzdCkiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygic2xhdGVibHVlMSIpKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihkZjMkRVZzUGVyQ2hhcmdlciksIGNvbG9yPSJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpDQpnZ3Bsb3RseShwbHRFVnZzQ2hhcmdlcnMsIHdpZHRoPTgwMCkgJT4lIGxheW91dChzaG93bGVnZW5kID0gRkFMU0UpDQpgYGANCiMjIyBJbnNpZ2h0IDYNCkNvdW50cmllcyBzdWNoIGFzIEdlcm1hbnksIE5vcndheSBhbmQgU3dlZGVuIGhhcyBoaWdoZXIgdGhhbiBhdmVyYWdlIG9mIEVWIHBlciBjaGFyZ2VyIHJhdGlvIChPbmUgY2hhcmdlciBoYXMgc2VydmVzIG1vcmUgRVZzKS4NCg0KDQojIyBTaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gb24gRVYgc2FsZXMgYW5kIEVWIGVsZWN0cmljaXR5IGRlbWFuZA0KDQpUbyBmb3JlY2FzdCBmdXR1cmUgRVYgZWxlY3RyaWNpdHkgZGVtYW5kLCB3ZSBtdXN0IGZpcnN0IGlkZW50aWZ5IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvdXIgcHJlZGljdG9yIHZhcmlhYmxlcyhCRVYgYW5kIFBIRVYgc2FsZXMpIGFuZCByZXNwb25zZSB2YXJpYWJsZSAoRVYgZWxlY3RyaWNpdHkgZGVtYW5kKS4NCg0KRmlyc3QsIHdlIHBlcmZvcm0gYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gb24gRVYgc2FsZXMgYXMgb3VyIHByZWRpY3RvciB2YXJpYWJsZSBhbmQgRVYgZWxlY3RyaWNpdHkgZGVtYW5kIGFzIG91ciByZXNwb25zZSB2YXJpYWJsZS4NCldlIHdpbGwgbWFuaXB1bGF0ZSB0aGUgRVYgc2FsZXMgZGF0YSBhbmQgdHVybiBpdCBpbnRvIGN1bXVsYXRpdmUgc3VtLCBzaW5jZSB0aGUgbmF0dXJlIG9mIEVWIGVsZWN0cmljaXR5IGRlbWFuZCBkYXRhIGlzIGN1bXVsYXRpdmUgKEVhY2ggZGF0YXBvaW50IG9mIEVWIGRlbWFuZCBpcyBjdW11bGF0aXZlIHRvIGFsbCBwcmV2aW91cyBFViBzb2xkLCBhcyB3ZSBhc3N1bWUgRVZzIHNvbGQgaW4gdGhlIHBhc3QgYWxzbyBjb250cmlidXRlIHRvd2FyZHMgdGhlIGRlbWFuZCkNCg0KYGBge3J9DQp1bmlxdWUoRVZkZW1hbmRfY2xlYW5lZCR5ZWFyKQ0KdW5pcXVlKEVWc2FsZXNfY2xlYW5lZCR5ZWFyKQ0KYGBgDQpXZSBjYW4gc2VlIHRoYXQgdGhlIEVWIGVsZWN0cmljaXR5IGRlbWFuZCBkYXRhc2V0IG9ubHkgaGF2ZSBFdXJvcGUgcmVnaW9uIGRhdGEgYXZhaWxhYmxlIGJldHdlZW4gMjAxNSBhbmQgMjAyMCwgd2Ugd2lsbCB1c2UgdGhpcyB0aW1lIHBlcmlvZCBmb3Igb3VyIHJlZ3Jlc3Npb24gYW5hbHlzZXMgbGF0ZXIgb24uDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCwgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkNCmxpYnJhcnkoTUFTUykNCmRmIDwtIGlubmVyX2pvaW4oRVZkZW1hbmRfY2xlYW5lZCAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcml6ZSgiRVZkZW1hbmQiID0gc3VtKHZhbHVlKSksIEVWc2FsZXNfY2xlYW5lZCAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcml6ZSgiRVZzYWxlcyIgPSBzdW0odmFsdWUpKSkNCmRmJGN1bXN1bUVWc2FsZXMgPC0gKGN1bXN1bShkZiRFVnNhbGVzKSkNCmRmDQpgYGANCg0KSGVyZSB3ZSBmaXJzdCB2aXN1YWxpemUgdGhlIGRhdGEgdmlhIGEgc2NhdHRlciBwbG90Og0KDQpgYGB7cn0NCnBsdF9sbTEgPC0gZ2dwbG90KGRmLGFlcyh4PWN1bXN1bUVWc2FsZXMseT1FVmRlbWFuZCkpKw0KICAgICAgICAgZ2VvbV9wb2ludCgpKw0KICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICBnZW9tX3Ntb290aChmb3JtdWxhID0geSB+IHgsIG1ldGhvZD1sbSxzZT1GQUxTRSxmdWxscmFuZ2U9VFJVRSkNCmdncGxvdGx5KHBsdF9sbTEpDQpgYGANCg0KVGhlIHBsb3Qgc2hvd3MgdGhhdCB0aGUgbGluZWFyIG1vZGVsIGZpdHMgYWxtb3N0IHBlcmZlY3RseSwgd2Ugd2lsbCBleGFtaW5lIHRoaXMgd2l0aCBhIGNvcnJlbGF0aW9uIHRlc3QuDQoNCmBgYHtyfQ0KY29yLnRlc3QoZGYkY3Vtc3VtRVZzYWxlcywgZGYkRVZkZW1hbmQpDQpgYGANCiMjIyBJbnNpZ2h0IDcNClRoZSBwIHZhbHVlIGZvciB0aGlzIG1vZGVsIGlzIHZlcnkgbG93ICg8MC4wNSkgYW5kIGhlbmNlIHdlIGNvdWxkIGFzc3VtZSBoaWdoIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSwgcmVqZWN0aW5nIHRoZSBudWxsIGh5cG90aGVzaXMuDQpUaGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgMC45OTk0ODA1IGFsc28gc3VnZ2VzdCBhIGhpZ2ggcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgY3VtdWxhdGl2ZSBzYWxlcyBvZiBFViBhbmQgdGhlIEVWIGVsZWN0cmljaXR5IGRlbWFuZC4NCg0KU2luY2Ugb3VyIGRhdGFzZXQgYXJlIGNvbXByaXNlZCBvZiBjYXRlZ29yaWNhbCBkYXRhIHN1Y2ggYXMgcmVnaW9uIGFuZCBwb3dlcnRyYWluLCB3ZSBuZWVkIHRvIHVzZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpbiBvcmRlciB0byBjcmVhdGUgYSBtb3JlIGFjY3VyYXRlIG1vZGVsIGFuZCBpbiB0dXJuIHByb2R1Y2UgbW9yZSBjb25maWRlbnQgZm9yZWNhc3QgcmVzdWx0cy5IZW5jZSB3ZSB3aWxsIHBlcmZvcm0gbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gb24gQkVWIHZzLiBCRVYgZWxlY3RyaWNpdHkgZGVtYW5kcyBhbmQgUEhFViB2cy4gUEhFViBlbGVjdHJpY2l0eSBkZW1hbmQuDQoNCiMjIE11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uDQoNCkZpcnN0LCB3ZSBjb25zdHJ1Y3QgYSBkYXRhZnJhbWUgZnJvbSB0aGUgY3VtdWxhdGl2ZSBzdW0gb2YgYW5udWFsIEJFViBhbmQgUEhFViBzYWxlcyBpbiBFdXJvcGVhbiBjb3VudHJpZXMgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgYW5udWFsIGVsZWN0cmljaXR5IGRlbWFuZC4gd2Ugd2lsbCBhbHNvIGNhbGN1bGF0ZSB0aGUgZWxlY3RyaWNpdHkgZGVtYW5kIHBlciB2ZWhpY2xlIGZvciBFViBhbmQgUEhFViBhbmQgY29udmVydCB0aGUgdW5pdCBpbnRvIEtpbG93YXR0IEhvdXIgKEtXaCkgZnJvbSB0aGUgb3JpZ2luYWwgR1doIGZyb20gdGhlIGRhdGFzZXQuDQpGQ0VWIHNhbGVzIGRhdGEgaXMgZXhjbHVkZWQgZnJvbSB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiBhbmFseXNpcyBhcyBFVmRlbWFuZCBkYXRhc2V0IGRvZXMgbm90IGNvbnRhaW4gcmVsZXZhbnQgZGF0YS4NCg0KDQpgYGB7cn0NCmRmNCA8LSBFVnNhbGVzX2NsZWFuZWQgJT4lIGdyb3VwX2J5KHllYXIsIHBvd2VydHJhaW4pICU+JSBzdW1tYXJpc2UoInZhbHVlIiA9IHN1bSh2YWx1ZSkpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gInBvd2VydHJhaW4iLCB2YWx1ZXNfZnJvbSA9ICJ2YWx1ZSIpICU+JSByZW5hbWUoc2FsZXNfQkVWID0gQkVWLCBzYWxlc19GQ0VWID0gRkNFViwgc2FsZXNfUEhFViA9IFBIRVYpDQpkZjQkY3Vtc3VtQkVWc2FsZXMgPC0gKGN1bXN1bShkZjQkc2FsZXNfQkVWKSkNCmRmNCRjdW1zdW1QSEVWc2FsZXMgPC0gKGN1bXN1bShkZjQkc2FsZXNfUEhFVikpDQpkZjQgPC0gc3Vic2V0KGRmNCwgc2VsZWN0ID0gLWMoc2FsZXNfQkVWLCBzYWxlc19QSEVWLCBzYWxlc19GQ0VWKSkNCmRmNyA8LSBFVmRlbWFuZF9jbGVhbmVkICU+JSBncm91cF9ieSh5ZWFyLCBwb3dlcnRyYWluKSAlPiUgc3VtbWFyaXNlKCJ2YWx1ZSIgPSBzdW0odmFsdWUpKSAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJwb3dlcnRyYWluIiwgdmFsdWVzX2Zyb20gPSAidmFsdWUiKSAlPiUgcmVuYW1lKEVWZGVtYW5kX0JFViA9IEJFViwgRVZkZW1hbmRfUEhFViA9IFBIRVYpDQpkZjckRVZkZW1hbmRfVG90YWwgPC0gZGY3JEVWZGVtYW5kX0JFViArIGRmNyRFVmRlbWFuZF9QSEVWDQpkZjYgPC0gbWVyZ2UoZGY0LCBkZjcsIGJ5PSJ5ZWFyIikNCmRmNiRFRHBlckJFViA8LSAoZGY2JEVWZGVtYW5kX0JFViAvIGRmNiRjdW1zdW1CRVZzYWxlcykgKiAxZTYNCmRmNiRFRHBlclBIRVYgPC0gKGRmNiRFVmRlbWFuZF9QSEVWIC8gZGY2JGN1bXN1bVBIRVZzYWxlcykgKiAxZTYNCmRmNg0KYGBgDQoNCkFsdGhvdWdoIHdlIGtub3cgdGhhdCBFViBzYWxlcyBhbmQgY29tYmluZWQgRWxlY3RyaWNpdHkgZGVtYW5kIGlzIHBvc2l0aXZlbHkgY29ycmVsYXRlZCwgd2Ugc3RpbGwgd2FudCB0byBmaW5kIG91dCBob3cgc3Ryb25nIHRoZSBjb3JyZWxhdGlvbiBpcyBmb3IgZWFjaCB0eXBlIG9mIHRoZSBFViBiYXNlZCBvbiBwb3dlcnRyYWluLiBXZSBjYW4gYWNoaWV2ZSB0aGF0IGJ5IHByb2R1Y2Ugc2NhdHRlciBwbG90cyBmb3IgYm90aCBCRVYgYW5kIFBIRVYgdG8gdmlzdWFsaXplIHRoZWlyIGxpbmVhciByZWxhdGlvbnNoaXA6DQoNCmBgYHtyfQ0Kc3BCRVYgPC0gcGxvdF9seSh4ID0gZGY2JGN1bXN1bUJFVnNhbGVzLCB5ID0gZGY2JEVWZGVtYW5kX0JFViwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdtYXJrZXJzJywgbmFtZSA9ICdCRVYnLA0KICAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QobGluZSA9IGxpc3Qod2lkdGggPSAzKSksIHdpZHRoPTgwMCkgJT4lDQogIGxheW91dChwbG90X2JnY29sb3I9JyNlNWVjZjYnLCANCiAgICAgICAgIHhheGlzID0gbGlzdCggDQogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAnI2ZmZmYnLCANCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIA0KICAgICAgICAgICBncmlkY29sb3IgPSAnZmZmZicpLCANCiAgICAgICAgIHlheGlzID0gbGlzdCggDQogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAnI2ZmZmYnLCANCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIA0KICAgICAgICAgICBncmlkY29sb3IgPSAnZmZmZicpKQ0KDQpzcFBIRVYgPC0gcGxvdF9seSh4ID0gZGY2JGN1bXN1bVBIRVZzYWxlcywgeSA9IGRmNiRFVmRlbWFuZF9QSEVWLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLCBuYW1lID0gJ1BIRVYnLA0KICAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QobGluZSA9IGxpc3Qod2lkdGggPSAzKSksIHdpZHRoPTgwMCkgJT4lDQogIGxheW91dChwbG90X2JnY29sb3I9JyNlNWVjZjYnLCANCiAgICAgICAgIHhheGlzID0gbGlzdCggDQogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAnI2ZmZmYnLCANCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIA0KICAgICAgICAgICBncmlkY29sb3IgPSAnZmZmZicpLCANCiAgICAgICAgIHlheGlzID0gbGlzdCggDQogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAnI2ZmZmYnLCANCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIA0KICAgICAgICAgICBncmlkY29sb3IgPSAnZmZmZicpKQ0KDQpzcDIgPC0gc3VicGxvdChzcEJFViwgc3BQSEVWLCBtYXJnaW4gPSAwLjA3KSAlPiUgDQogIGxheW91dCh0aXRsZSA9ICdFViBzYWxlcyB2cyBFViBlbGVjdHJpY2l0eSBEZW1hbmQgKDIwMTUtMjAyMCwgRXVyb3BlKScpDQpzcDINCmBgYA0KDQpgYGB7cn0NCmNvcihkZjYkY3Vtc3VtQkVWc2FsZXMsIGRmNiRFVmRlbWFuZF9CRVYpDQpjb3IoZGY2JGN1bXN1bVBIRVZzYWxlcywgZGY2JEVWZGVtYW5kX1BIRVYpDQpgYGANCkNvcnJlbGF0aW9uIHRlc3Qgc2hvd3MgdGhhdCBib3RoIHJlbGF0aW9uc2hpcCBhcmUgYWxtb3N0IHBlcmZlY3RseSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQuIA0KDQpTaW5jZSB3ZSBhbHNvIGNhbGN1bGF0ZWQgdGhlIGVsZWN0cmljaXR5IGRlbWFuZCBwZXIgdmVoaWNsZSBmb3IgRVYgYW5kIFBIRVYsIHdlIGNhbiB0aGVuIGZ1cnRoZXIgYW5hbHl6ZSBhbmQgdmlzdWFsaXplIGJ5IHByb2R1Y2luZyBhIGJveHBsb3QgYmFzZWQgb24gdGhlc2UgZGF0YS4NCg0KYGBge3J9DQpicEVEcGVyQkVWIDwtIHBsb3RfbHkoeSA9IGRmNiRFRHBlckJFViwgdHlwZSA9ICJib3giLCBuYW1lID0gJ0JFViBFbGVjdHJpY2l0eSBEZW1hbmQnLCAgd2lkdGg9ODAwKQ0KYnBFRHBlclBIRVYgPC0gcGxvdF9seSh5ID0gZGY2JEVEcGVyUEhFViwgdHlwZSA9ICJib3giLCBuYW1lID0gJ1BIRVYgRWxlY3RyaWNpdHkgRGVtYW5kJywgIHdpZHRoPTgwMCkNCmJwMSA8LSBzdWJwbG90KGJwRURwZXJCRVYsIGJwRURwZXJQSEVWLCBtYXJnaW4gPSAwLjA3KSAlPiUgDQogIGxheW91dCh0aXRsZSA9ICdBdmVyYWdlIEFubnVhbCBFbGVjdHJpY2l0eSBEZW1hbmQgcGVyIEVWIChLV2gpLCAyMDE1LTIwMjAnKQ0KYnAxDQpgYGANCiMjIyBJbnNpZ2h0IDgNClRoZSBBdmVyYWdlIGFubnVhbCBlbGVjdHJpY2l0eSBkZW1hbmQgcGVyIEJFViBoYXMgYSBoaWdoZXIgcmFuZ2UgdGhhbiB0aGUgZGVtYW5kIG9mIFBIRVYuIFRoZSBtZWFuIG9mIEJFViBlbGVjdHJpY2l0eSBkZW1hbmQgaW4gdGVybXMgb2Yga2lsb3dhdHQgaG91ciBpcyBhbHNvIGhpZ2hlciB0aGFuIFBIRVYncywgd2hpY2ggaXMgdG8gYmUgZXhwZWN0ZWQgc2luY2UgUEhFViB1c3VhbGx5IGhhdmUgYSBzbWFsbGVyIGNhcGFjaXR5IGJhdHRlcnkgdGhhbiBtb3N0IEJFVi4NClRoZXJlIGlzIGFsc28gYW4gb3V0bGllciBpbiBQSEVWIGVsZWN0cmljaXR5IGRlbWFuZCB0aGF0IGlzIHNpZ25pZmljYW50bHkgYmVsb3cgaXRzIHVzdWFsIHZhbHVlLg0KVGhlIGludGVycXVhcnRpbGUgcmFuZ2Ugb2YgQkVWIGVsZWN0cmljaXR5IGRlbWFuZCBpcyBsYXJnZXIgdGhhbiBpdHMgUEhFViBjb3VudGVycGFydC4NCg0KTmV4dCwgd2UgY29uc3RydWN0IHRoZSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsLg0KDQpgYGB7cn0NCm1vZGVsIDwtIGxtKEVWZGVtYW5kX1RvdGFsIH4gY3Vtc3VtQkVWc2FsZXMgKyBjdW1zdW1QSEVWc2FsZXMsIGRhdGEgPSBkZjYpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQojIyMgSW5zaWdodCA5DQoNClRoZSBwIHZhbHVlIGhlcmUgaXMgaGlnaGx5IHNpZ25pZmljYW50IHdoaWNoIG1lYW5zIHRoYXQgYXQgbGVhc3Qgb25lIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGlzIHNpZ25pZmljYW50bHkgcmVsYXRlZCB0byB0b3RhbCBFViBlbGVjdHJpY2l0eSBkZW1hbmQuDQpUaGUgYWRqdXN0ZWQgUiBzcXVhcmVkIHZhbHVlIG9mIDAuOTk4NyBtZWFucyB0aGF0IDk5Ljg3JSBvZiB0aGUgdmFyaWFuY2UgY291bGQgYmUgZXhwbGFpbmVkIGJ5IGN1bXVsYXRpdmUgRVYgc2FsZXMuDQpUaGUgUmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgdGVsbHMgdXMgdGhhdCB0aGUgbW9kZWwgcHJlZGljdHMgdGhlIGVsZWN0cmljaXR5IGRlbWFuZCBvZiBFVnMgd2l0aCBhbiBhdmVyYWdlIGVycm9yIG9mIGFib3V0IDg2LjE5IEdXaC4NClRoZSBlc3RpbWF0ZWQgcmVncmVzc2lvbiBsaW5lIGVxdWF0aW9uIGlzIHJvdWdobHk6IEVWZGVtYW5kX1RvdGFsID0gLTE4MC41NCArIDAuMDAxNTQgKiBjdW1zdW1CRVZzYWxlcyArIDAuMDAzMjAgKiBjdW1zdW1QSEVWc2FsZXMNCg0KDQoNCiMjIFRpbWUgc2VyaWVzIGFuYWx5c2lzIHVzaW5nIEFSSU1BIG1vZGVsbGluZw0KDQpBcyB3ZSBoYXZlIGEgbW9kZWwgcmVhZHkgdG8gZm9yZWNhc3QgRVYgZWxlY3RyaWNpdHkgZGVtYW5kLCB3ZSBhbHNvIG5lZWQgZm9yZWNhc3QgZGF0YSBmb3IgRVYgc2FsZXMgdG8gZml0IHRvIHRoZSBtb2RlbCBhcyBuZXcgZGF0YS4NCkZpcnN0LCB3ZSBtYW5pcHVsYXRlIHRoZSBkYXRhIGludG8gYSBuZXcgZGF0YWZyYW1lLiBXZSB3YW50IG91ciBFViBzYWxlcyBkYXRhIHRvIGJlIHBpdm90ZWQgd2lkZSBpbiBvcmRlciB0byBydW4gdGhpcyB0aHJvdWdoIGEgbG9vcC4NCmBgYHtyfQ0KZGY4IDwtIEVWc2FsZXNfY2xlYW5lZCAlPiUgZmlsdGVyKHBvd2VydHJhaW4gIT0iRkNFViIpICU+JSBncm91cF9ieSh5ZWFyLCBwb3dlcnRyYWluLCByZWdpb24pICU+JSBzdW1tYXJpc2UoInZhbHVlIiA9IHN1bSh2YWx1ZSkpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gInJlZ2lvbiIsIHZhbHVlc19mcm9tID0gInZhbHVlIiwgdmFsdWVzX2ZpbGwgPSAwKQ0KZGY4IDwtIGRmOCAlPiUgcGl2b3RfbG9uZ2VyKCdEZW5tYXJrJzonR3JlZWNlJywgbmFtZXNfdG8gPSAicmVnaW9uIiwgdmFsdWVzX3RvID0gInZhbHVlIikNCmRmOCRwb3dlcnRyYWlucmVnaW9uIDwtIHBhc3RlKGRmOCRwb3dlcnRyYWluLCBkZjgkcmVnaW9uKQ0KZGY4IDwtIGRmOCAlPiUgdW5ncm91cCgpDQpkZjggPC0gc3Vic2V0KGRmOCwgc2VsZWN0ID0gLWMocmVnaW9uLCBwb3dlcnRyYWluKSkNCmRmOCA8LSBkZjggJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAicG93ZXJ0cmFpbnJlZ2lvbiIsIHZhbHVlc19mcm9tID0gInZhbHVlIikNCmhlYWQoZGY4KQ0KYGBgDQoNClRoZSBkYXRhIGlzIHN0aWxsIGluIGFubnVhbGl6ZWQgc2FsZXMgZGF0YSwgd2UgbmVlZCBjdW11bGF0aXZlIHN1bSBvZiB0aGUgc2FsZXMgZGF0YSBpbiBvcmRlciB0byBwZXJmb3JtIGF1dG8uYXJpbWEgZnVuY3Rpb24uDQpIZXJlIHdlIGxvb3AgdGhyb3VnaCB0aGUgZGF0YWZyYW1lIHRvIGNhbGN1bGF0ZSBjdW11bGF0aXZlIHN1bS4NCg0KYGBge3J9DQpkZjkgPC0gYXMuZGF0YS5mcmFtZShkZjgkeWVhcikNCm5hbWVzKGRmOSlbMV0gPC0gJ3llYXInDQpmb3IoaSBpbiAyOm5jb2woZGY4KSl7DQogIGRmOVtpXSA8LSBjdW1zdW0oZGY4WyAsIGMoaSldKQ0KfQ0KZGY5IDwtIGRmOSAlPiUgcmVuYW1lX2FsbChmdW5jdGlvbih4KSBnc3ViKCIgIiwgIl8iLCB4KSkNCmhlYWQoZGY5KQ0KYGBgDQoNCk5vdyB3ZSBoYXZlIG91ciBkYXRhIHJlYWR5LCBpdCBpcyB0aW1lIHRvIGxvb3AgdGhyb3VnaCBpdCBvbmNlIG1vcmUgd2l0aCBhcmltYSBhbmQgZm9yZWNhc3QgZnVuY3Rpb25zLg0KDQpgYGB7cn0NCkVWc2FsZXNQcm9qZWN0aW9uIDwtIGRhdGEuZnJhbWUobWF0cml4KDAsIG5yb3cgPSAxMCwgbmNvbCA9IDM1KSkNCmNvbG5hbWVzKEVWc2FsZXNQcm9qZWN0aW9uKSA8LSBjb2xuYW1lcyhkZjkpDQpFVnNhbGVzUHJvamVjdGlvbiR5ZWFyIDwtIGMoMjAyMToyMDMwKQ0KZm9yKGkgaW4gMjpuY29sKGRmOSkpew0KICBFVnNhbGVzUHJvamVjdGlvbltpXSA8LSBmb3JlY2FzdChhdXRvLmFyaW1hKGRmOVtpXSkpJG1lYW4NCn0NCkVWc2FsZXNQcm9qZWN0aW9uDQpgYGANCg0KTm93IHRoYXQgd2UgaGF2ZSBvdXIgcHJvamVjdGVkIHNhbGVzIGRhdGEsIHdlIHdpbGwgcnVuIHRoZW0gdGhyb3VnaCB5ZXQgYW5vdGhlciBsb29wIHRvIGZpdCBuZXcgZGF0YSB0byBvdXIgcmVncmVzc2lvbiBtb2RlbC4NCg0KYGBge3J9DQpmaXQgPC0gbG0oRVZkZW1hbmRfVG90YWwgfiBjdW1zdW1CRVZzYWxlcyArIGN1bXN1bVBIRVZzYWxlcywgZGF0YSA9IGRmNikNCmZjYXN0ZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgoMCwgbnJvdyA9IDEwLCBuY29sID0gMTgpKQ0KY29sbmFtZXMoZmNhc3RkZikgPC0gY29sbmFtZXMoRVZzYWxlc1Byb2plY3Rpb24pWzE6MThdDQpuYW1lcyhmY2FzdGRmKSA8LSBzdWJzdHJpbmcobmFtZXMoZmNhc3RkZiksIDUpDQpmY2FzdGRmWzFdIDwtIDIwMjE6MjAzMA0KZmNhc3RkZiA8LSByZW5hbWUoZmNhc3RkZiwgeWVhciA9ICIiKQ0KDQpmb3IoaSBpbiAyOjE4KXsNCnRlbXBkZiA8LSBkYXRhLmZyYW1lKG1hdHJpeCgwLCBucm93ID0gMTAsIG5jb2wgPSAyKSkNCnRlbXBkZiA8LSBzdWJzZXQoRVZzYWxlc1Byb2plY3Rpb24sIHNlbGVjdCA9IGMoaSxpKzE3KSkNCnRlbXBkZiA8LSB0ZW1wZGYgJT4lIHJlbmFtZShjdW1zdW1CRVZzYWxlcyA9IDEsIGN1bXN1bVBIRVZzYWxlcyA9IDIpDQpmY2FzdGRmW2ldIDwtIChmb3JlY2FzdChmaXQsIG5ld2RhdGE9dGVtcGRmKSkkbWVhbg0KfQ0KaGVhZChmY2FzdGRmKQ0KYGBgDQoNCkhlcmUgaXMgYSBzaW1wbGUgdmlzdWFsaXphdGlvbiBpbiBSLCB3ZSB3aWxsIGFsc28gZXhwb3J0IGFuZCB2aXN1YWxpemUgdGhlIGZvcmVjYXN0IGRhdGFzZXQgaW4gVGFibGVhdS4NCg0KYGBge3J9DQpwbHRfZmNhc3RFVmRlbWFuZCA8LSBkYXRhLmZyYW1lKGZjYXN0ZGYgJT4lIGdyb3VwX2J5KHllYXIpICU+JSBwaXZvdF9sb25nZXIoMjoxOCwgbmFtZXNfdG8gPSAicmVnaW9uIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lIGdyb3VwX2J5KHllYXIscmVnaW9uKSAlPiUgc3VtbWFyaXplKGVsZWN0cmljaXR5X2RlbWFuZCA9IHN1bSh2YWx1ZSkpKQ0KcGx0X2ZjYXN0RVZkZW1hbmQgPC0gcGx0X2ZjYXN0RVZkZW1hbmQgJT4lIGdncGxvdChhZXMoeD15ZWFyLCB5PWVsZWN0cmljaXR5X2RlbWFuZCwgY29sb3I9cmVnaW9uKSkgKw0KICBnZW9tX2xpbmUoKSsNCiAgbGFicyh4ID0gIlllYXIiLCANCiAgICAgICB5ID0gIk51bWJlcnMgb2YgRVYgc29sZCIsDQogICAgICAgdGl0bGUgPSAiRVYgZWxlY3RyaWNpdHkgZGVtYW5kIGZvcmVjYXN0IGluIEdXaCAoMjAyMS0yMDMwKSIpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAyMDIxOjIwMzApDQpnZ3Bsb3RseShwbHRfZmNhc3RFVmRlbWFuZCwgd2lkdGg9ODAwKQ0KYGBgDQojIyMgSW5zaWdodCAxMA0KQmFzZWQgb24gb3VyIGZvcmVjYXN0IG1vZGVsLCBHZXJtYW55LCBGcmFuY2UgYW5kIHRoZSBVbml0ZWQgS2luZ2RvbSBhcmUgdGhlIHRvcCAzIGhpZ2hlc3QgY291bnRyeSBvZiBFViBlbGVjdHJpY2l0eSBkZW1hbmQgYnkgMjAzMC4NCg0KV2Ugbm90aWNlIHRoYXQgR3JlZWNlLCBJY2VsYW5kIGFuZCBQb2xhbmQncyBwcmVkaWN0ZWQgZGF0YSBjb250YWlucyBhIGxvdCBvZiBuZWdhdGl2ZSB2YWx1ZS4gVGhlIHJlYXNvbiBjb3VsZCBiZSB0aGF0IHZhbHVlIG9mIHNhbGVzIG9mIEVWIGluIHRoZXNlIGNvdW50cmllcyBhcmUgYXQgdGhlIGxvd2VzdCBlbmQgb2Ygb3VyIGRhdGFzZXQgYW5kIGNvdWxkIGJlIGNvbnNpZGVyZWQgb3V0bGllci4gU2luY2Ugb3VyIG1vZGVsIGlzIGJ1aWx0IHVwb24gdGhlIGFnZ3JlZ2F0ZWQgZWxlY3RyaWNpdHkgZGVtYW5kIG9mIHRoZSB3aG9sZSBFdXJvcGUsIHRoZSBhY2N1cmFjeSBvZiBwcmVkaWN0aW9ucyB3b3VsZCBiZSBsb3cuIEhlbmNlLCB0aGV5IHdpbGwgYmUgZXhjbHVkZWQgZnJvbSB0aGUgdmlzdWFsaXphdGlvbiBzaW5jZSBuZWdhdGl2ZSB2YWx1ZXMgb2YgZWxlY3RyaWNpdHkgZGVtYW5kIGRvZXMgbm90IG1ha2Ugc2Vuc2UuDQpXZSB3aWxsIGFsc28gZXhjbHVkZSAiT3RoZXJfRXVyb3BlIiBmcm9tIHRoZSB2aXN1YWxpemF0aW9uIHNpbmNlIHRoZSBwdWJsaXNoZXIgb2YgdGhlIGRhdGEgZGlkIG5vdCBzcGVjaWZ5IHdoaWNoIHJlZ2lvbnMgaXQgY292ZXJlZC4NCg0KYGBge3J9DQpmY2FzdGRmX2ZpbmFsIDwtIHN1YnNldChmY2FzdGRmLCBzZWxlY3QgPSAtYyhHcmVlY2UsIEljZWxhbmQsIFBvbGFuZCwgT3RoZXJfRXVyb3BlKSkNCmZjYXN0ZGZfZmluYWwgPC0gZmNhc3RkZl9maW5hbCAlPiUgcGl2b3RfbG9uZ2VyKDI6MTQsIG5hbWVzX3RvID0gInJlZ2lvbiIsIHZhbHVlc190byA9ICJ2YWx1ZSIpDQpoZWFkKGZjYXN0ZGZfZmluYWwpDQpgYGANCg0KRXhwb3J0IHRoZSBkYXRhZnJhbWUgYXMgLmNzdiBmaWxlIGZvciB2aXN1YWxpemF0aW9uIGluIFRhYmxlYXU6DQoNCmBgYHtyfQ0Kd3JpdGVfY3N2KGZjYXN0ZGZfZmluYWwsICJFVkVsZWN0cmljaXR5RGVtYW5kUHJvamVjdGVkMjAyMV8yMDMwLmNzdiIpDQpgYGANCg0KDQohW1RoaXMgaXMgdGhlIFRhYmxlYXUgdmlzdWFsaXphdGlvbiBvZiB0aGUgcHJvamVjdGVkIEVWIGVsZWN0cmljaXR5IGRlbWFuZCBkYXRhc2V0LiBBdmFpbGFibGUgZmlsdGVycyBpbmNsdWRlcyBhIGNvdW50cnkgZmlsdGVyIGFuZCBzbGlkZXIgeWVhciBmaWx0ZXIuIFZhbHVlIG1lYXN1cmUgaXMgaW4gR2lnYXdhdHQgaG91ci4gV2FybWVyIGNvbG91ciBzdWdnZXN0cyBoaWdoIGRlbWFuZCBhbmQgY29sZGVyIGNvbG91ciBzdWdnZXN0cyBsb3cgZGVtYW5kLl0odGFibGVhdXZpejIucG5nKQ0KDQoNCg0KIyMgU2hhcmUNCg0KVGhpcyBpcyB0aGUgZGV0YWlsZWQgZG9jdW1lbnQgaW4gUiBub3RlYm9vaywgd2l0aCBldmVyeSBwcm9jZWR1cmUgYW5kIGNvZGUgZG9jdW1lbnRlZCBhbmQgaW50ZXJhY3RpdmUgUGxvdGx5IHZpc3VhbGl6YXRpb24uDQpUaGUgR29vZ2xlIFNsaWRlcyB2ZXJzaW9uIChubyBjb2Rlcywgb25seSB2aXN1YWxpemF0aW9uLCBpbnNpZ2h0cyBhbmQgcmVjb21tZW5kYXRpb24pIGlzIGF2YWlsYWJsZSBbaGVyZS5dKHd3dy5nb29nbGUuY29tKQ0KSW50ZXJhY3RpdmUgVGFibGVhdSBWaXogd29ya3NoZWV0IGlzIGF2YWlsYWJsZSBbaGVyZS5dKGh0dHBzOi8vcHVibGljLnRhYmxlYXUuY29tL2FwcC9wcm9maWxlL3J1aXBlbmcueXUvdml6L0VWRWxlY3RyaWNpdHlEZW1hbmRGb3JlY2FzdGluR1doMjAyMS0yMDMwUGFydHNvZkV1cm9wZS9TaGVldDEpDQoNCiMjIEFjdA0KDQojIyMgS2V5IGZpbmRpbmdzDQoNCiogRXVyb3BlYW4gRVYgbWFya2V0IGhhcyBleHBlcmllbmNlZCBhIGNvbnNpc3RlbnQgZ3Jvd3RoIGFjY29yZGluZyB0byBhdmFpbGFibGUgZGF0YSB0aW1lIGZyYW1lICgyMDEwLTIwMjApLg0KKiBGcm9tIDIwMTYgb253YXJkcywgdGhlIHRvdGFsIHNhbGVzIG9mIEVWcyBoYXMgc2VlbiBhY2NlbGVyYXRlZCBncm93dGggeWVhciBvbiB5ZWFyLg0KKiBHZXJtYW55LCBGcmFuY2UgYW5kIHRoZSBVSyBzZWVtcyB0byBsZWFkcyB0aGUgRVYgc2FsZXMgZmlndXJlIGFuZCBncm93dGggZmFjdG9yIGluIEV1cm9wZS4NCiogQmF0dGVyeSBFbGVjdHJpYyBWZWhpY2xlIChCRVYpIGRvbWluYXRlcyB0aGUgbWFya2V0IHdpdGggNTQuNyUgb2YgdGhlIHRvdGFsIEVWIHNhbGVzLCB3aXRoIFBsdWctaW4gSHlicmlkIEVsZWN0cmljIFZlaGljbGUgKFBIRVYpIGJlaGluZCB3aXRoIDQ1LjMlLg0KKiBGdWVsIENlbGwgRWxlY3RyaWMgdmVoaWNsZShGQ0VWKSBzZWVtcyB0byBiZSBhIG5pY2hlIGNob2ljZSBmb3IgY29uc3VtZXJzIGFzIHRoZXkgb25seSBhY2NvdW50cyBmb3IgMC4wNyUgb2YgdGhlIHRvdGFsIHNhbGVzLg0KKiBBcyBvZiAyMDIwLCBmYXN0IGNoYXJnZXIgc2VlbXMgdG8gYmUgbGFja2luZyBhcyBvbmx5IDEgb3V0IG9mIDEwIHB1YmxpY2x5IGF2YWlsYWJsZSBjaGFyZ2VycyBpbiBFdXJvcGUgaXMgYSBmYXN0IGNoYXJnZXIuDQoqIFB1YmxpYyBjaGFyZ2VycyBpbiBjb3VudHJpZXMgc3VjaCBhcyBOZXRoZXJsYW5kcywgSXRhbHkgYW5kIEJlbGdpdW0gYXJlIG1vc3RseSBzbG93IGNoYXJnZXJzLCB3aGlsZSBHZXJtYW55LCBOb3J3YXkgYW5kIFN3ZWRlbiBoYXMgaGlnaGVyIHRoYW4gYXZlcmFnZSBvZiBFViBwZXIgY2hhcmdlciByYXRpby4NCiogT24gYXZlcmFnZSwgZWFjaCBCRVYgY3JlYXRlcyBtb3JlIGVsZWN0cmljaXR5IGRlbWFuZCB0aGFuIFBIRVYuDQoqIEFjY29yZGluZyB0byBvdXIgbW9kZWwsIEdlcm1hbnksIEZyYW5jZSBhbmQgdGhlIFVLIHdpbGwgYmUgdGhlIHRvcCAzIEV1cm9wZWFuIGNvdW50cmllcyBvZiBoaWdoZXN0IEVWIGVsZWN0cmljaXR5IGRlbWFuZCBieSAyMDMwLg0KDQoNCiMjIyBSZWNvbW1lbmRhdGlvbnMNCg0KMS4gR2VybWFueSwgRnJhbmNlIGFuZCB0aGUgVUsgYXJlIHRoZSBwcm9taW5lbnQgdGFyZ2V0IGZvciBlbmVyZ3kgaW52ZXN0bWVudHMgYXMgdGhleSBhcmUgdGhlIGxlYWRlcnMgaW4gRVYgbWFya2V0IGdyb3d0aCBpbiBFdXJvcGUuDQoyLiBCYXR0ZXJ5IEVsZWN0cmljIFZlaGljbGUgKEJFVikgYW5kIFBsdWctaW4gSHlicmlkIEVsZWN0cmljIFZlaGljbGUgKFBIRVYpIGRvbWluYXRlcyB0aGUgbWFya2V0IGFuZCBGdWVsIENlbGwgRWxlY3RyaWMgdmVoaWNsZShGQ0VWKSByZW1haW5zIHRvIGJlIG5pY2hlIGFuZCBpcnJlbGV2YW50IGFzIG9mIDIwMjAuIEhlbmNlLCBpbnZlc3RtZW50IHByb2plY3RzIGluIGNoYXJnaW5nIHNvbHV0aW9ucyBzaG91bGQgb25seSBjb25zaWRlciBFViBjaGFyZ2luZyBpbnN0ZWFkIG9mIGh5ZHJvZ2VuIG9yIG90aGVyIGZ1ZWwgdHlwZXMuDQozLiBGYXN0IGNoYXJnZXJzIGFyZSByYXJlIGFuZCB0aGV5IGFyZSBhIGNydWNpYWwgdG8gZW5hYmxlIGxvbmcgZGlzdGFuY2UgZHJpdmluZyAoRnVua2UsIFNwcmVpLCBHbmFubiBhbmQgUGzDtnR6LCAyMDE5KS4gQ29uc2lkZXIgaW52ZXN0IGluIGZhc3QgY2hhcmdpbmcgc29sdXRpb24gcHJvamVjdHMgaW4gdGhlc2UgY291bnRyaWVzIHdoZXJlIHRoZXkgbmVlZCB0aGUgbW9zdDogTmV0aGVybGFuZHMsIEl0YWx5IGFuZCBCZWxnaXVtLg0KNC4gU29tZSBjb3VudHJpZXMgbGFja3MgcHVibGljIGNoYXJnaW5nIGFsbCB0b2dldGhlcjogR2VybWFueSwgTm9yd2F5IGFuZCBTd2VkZW4sIHRoZXJlZm9yZSBpbnZlc3QgaW4gYW55IHB1YmxpYyBjaGFyZ2luZyBwcm9qZWN0IHdvdWxkIHN1ZmZpY2UuDQo1LiBHZXJtYW55LCBGcmFuY2UgYW5kIHRoZSBVSyBhcmUgdGhlIHRvcCAzIGNvdW50cmllcyB3aXRoIGhpZ2hlc3QgcHJvamVjdGVkIEVWIGVsZWN0cmljaXR5IGRlbWFuZCBieSAyMDMwLCB0aGVyZWZvcmUgbG9uZyB0ZXJtIGluZnJhc3RydWN0dXJlIHByb2plY3RzIGluIHRoZXNlIHJlZ2lvbnMgY291bGQgYmUgY29uc2lkZXJlZC4NCg0KIyMjIExpbWl0YXRpb25zIG9mIHRoZSBkYXRhIGFuZCB0aGUgYW5hbHlzaXMNCg0KQmFzZWQgb24gdGhlIHF1YWxpdHkgY29udHJhaW50cyBvZiB0aGUgZGF0YXNldHMgdXNlZCwgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIGxpbWl0YXRpb25zIHRvIGJlIGFkZHJlc3NlZDoNCg0KKiBTb21lIHJlZ2lvbmFsIGRhdGEgaXMgbWlzc2luZyBmcm9tIG9uZSBvciBtb3JlIGRhdGFzZXRzLCBtYWtpbmcgY3Jvc3MgcmVmZXJlbmNlIGRpZmZpY3VsdCBhcyB3ZSBoYWQgdG8gZXhjbHVkZSB0aGUgbWlzc2luZyByZWdpb24gZnJvbSB0aGUgYW5hbHlzaXMuDQoqIFRoZSBkYXRhIHBvaW50IGZyZXF1ZW5jeSBpbiB0aGUgZGF0YXNldHMgKGFubnVhbCkgY291bGQgY29tcHJvbWlzZSB0aGUgYWNjdXJhY3kgb2Ygb3VyIGFuYWx5c2lzLCBlc3BlY2lhbGx5IHRvIHRoZSBBUklNQSBtb2RlbC4NCiogRWxlY3RyaWNpdHkgZGVtYW5kIGRhdGFzZXQgZG9lcyBub3QgaGF2ZSBkYXRhIGRyaWxsZWQgZG93biB0byBlYWNoIGluZGl2aWR1YWwgY291bnRyeS4gSW5zdGVhZCwgd2UgbW9kZWxsZWQgb3VyIG11bHRpcGxlIHJlZ3Jlc3Npb24gYmFzZWQgb24gdGhlIGZpZ3VyZXMgb2Ygd2hvbGUgZXVyb3BlYW4gRVYgZWxlY3RyaWNpdHkgZGVtYW5kLiBUaGlzIG1lYW5zIHRoYXQgY291bnRyaWVzIHdpdGggZXh0cmVtZWx5IHNtYWxsL2xhcmdlIHZhbHVlcyBvZiBFViBzYWxlcyB3b3VsZCBiZSBwcm9uZSB0byBvdmVyZml0dGluZyBpc3N1ZXMgaW4gdGhlIHJlc3VsdHMgb2Ygb3VyIEVWIGVsZWN0cmljaXR5IGRlbWFuZCBmb3JlY2FzdC4NCiogVGhlcmUgYXJlIG5vIGF2YWlsYWJsZSBkYXRhIGZvciBwcml2YXRlIGNoYXJnZXJzLCB3aGljaCBjb3VsZCBiZSBhbiBpbXBvcnRhbnQgZmFjdG9yIGluIHRoZSBzdXBwbHkgYW5kIGRlbWFuZCBvZiBjaGFyZ2luZyBzdGF0aW9ucy4gVGhpcyBhbmFseXNpcyBwcmVzdW1lZCBwdWJsaWMgY2hhcmdlcnMgYWNjb3VudHMgZm9yIG1vc3Qgb2YgdGhlIGNoYXJnaW5nIGRlbWFuZCBvZiBFVnMuDQoNCg0KDQojIyBSZWZlcmVuY2UNCg0KSUVBLiAyMDIyLiBHbG9iYWwgRVYgRGF0YSBFeHBsb3JlciDigJMgQW5hbHlzaXMgLSBJRUEuIFtvbmxpbmVdIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LmllYS5vcmcvYXJ0aWNsZXMvZ2xvYmFsLWV2LWRhdGEtZXhwbG9yZXI+IFtBY2Nlc3NlZCA2IEphbnVhcnkgMjAyMl0uIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQoNCkVuZXJneS5nb3YuIDIwMjIuIFRoZSBIaXN0b3J5IG9mIHRoZSBFbGVjdHJpYyBDYXIuIFtvbmxpbmVdIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LmVuZXJneS5nb3YvYXJ0aWNsZXMvaGlzdG9yeS1lbGVjdHJpYy1jYXI+IFtBY2Nlc3NlZCA2IEphbnVhcnkgMjAyMl0uDQoNClBvcGtvc3RvdmEsIFkuLCAyMDIyLiBFVVJPUEXigJlTIEVORVJHWSBDUklTSVMgQ09OVU5EUlVNLiBbb25saW5lXSBJc3MuZXVyb3BhLmV1LiBBdmFpbGFibGUgYXQ6IDxodHRwczovL3d3dy5pc3MuZXVyb3BhLmV1L3NpdGVzL2RlZmF1bHQvZmlsZXMvRVVJU1NGaWxlcy9CcmllZl8yX0VuZXJneSUyMENyaXNpc193ZWIucGRmPiBbQWNjZXNzZWQgMiBNYXJjaCAyMDIyXS4NCg0KRnVua2UsIFMuLCBTcHJlaSwgRi4sIEduYW5uLCBULiBhbmQgUGzDtnR6LCBQLiwgMjAxOS4gSG93IG11Y2ggY2hhcmdpbmcgaW5mcmFzdHJ1Y3R1cmUgZG8gZWxlY3RyaWMgdmVoaWNsZXMgbmVlZD8gQSByZXZpZXcgb2YgdGhlIGV2aWRlbmNlIGFuZCBpbnRlcm5hdGlvbmFsIGNvbXBhcmlzb24uIFRyYW5zcG9ydGF0aW9uIFJlc2VhcmNoIFBhcnQgRDogVHJhbnNwb3J0IGFuZCBFbnZpcm9ubWVudCwgNzcsIHBwLjIyNC0yNDIuDQoNCg0KDQo=